Skip to content

Commit ca6dffb

Browse files
authored
Merge branch 'master' into nested-assumes-on-3x
2 parents 8323e8d + 0f577e2 commit ca6dffb

13 files changed

Lines changed: 101 additions & 55 deletions

File tree

.github/workflows/test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,4 @@ jobs:
102102
run: pip install tox
103103
- name: Run integration
104104
# Force one single concurrent test
105-
run: tox -e integration -- -n 1
105+
run: tox -e integration

juju/client/connection.py

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import json
66
import logging
77
import ssl
8+
import signal
89
import urllib.request
910
import weakref
1011
from http.client import HTTPSConnection
@@ -217,7 +218,7 @@ def status(self):
217218
if connection.is_debug_log_connection:
218219
stopped = connection._debug_log_task.cancelled()
219220
else:
220-
stopped = connection._receiver_task.cancelled()
221+
stopped = connection._receiver_task is not None and connection._receiver_task.cancelled()
221222

222223
if stopped or not connection._ws.open:
223224
return self.ERROR
@@ -418,6 +419,14 @@ async def _open(self, endpoint, cacert):
418419
sock = self.proxy.socket()
419420
server_hostname = "juju-app"
420421

422+
def _exit_tasks():
423+
for task in jasyncio.all_tasks():
424+
task.cancel()
425+
426+
loop = jasyncio.get_running_loop()
427+
for sig in (signal.SIGINT, signal.SIGTERM):
428+
loop.add_signal_handler(sig, _exit_tasks)
429+
421430
return (await websockets.connect(
422431
url,
423432
ssl=self._get_ssl(cacert),
@@ -431,25 +440,41 @@ async def close(self, to_reconnect=False):
431440
return
432441
self.monitor.close_called.set()
433442

443+
# Cancel all the tasks (that we started):
434444
if self._pinger_task:
435445
self._pinger_task.cancel()
436-
self._pinger_task = None
437446
if self._receiver_task:
438447
self._receiver_task.cancel()
439-
self._receiver_task = None
440448
if self._debug_log_task:
441449
self._debug_log_task.cancel()
442-
self._debug_log_task = None
443-
# Allow a second for tasks to be cancelled
444-
await jasyncio.sleep(1)
445450

446451
if self._ws and not self._ws.closed:
447452
await self._ws.close()
448-
self._ws = None
453+
454+
if not to_reconnect:
455+
try:
456+
log.debug('Gathering all tasks for connection close')
457+
458+
# Avoid gathering the current task
459+
tasks_need_to_be_gathered = [task for task in jasyncio.all_tasks() if task != jasyncio.current_task()]
460+
await jasyncio.gather(*tasks_need_to_be_gathered)
461+
except jasyncio.CancelledError:
462+
pass
463+
except websockets.exceptions.ConnectionClosed:
464+
pass
465+
466+
self._pinger_task = None
467+
self._receiver_task = None
468+
self._debug_log_task = None
449469

450470
if self.proxy is not None:
451471
self.proxy.close()
452472

473+
# Remove signal handlers
474+
loop = jasyncio.get_running_loop()
475+
for sig in (signal.SIGINT, signal.SIGTERM):
476+
loop.remove_signal_handler(sig)
477+
453478
async def _recv(self, request_id):
454479
if not self.is_open:
455480
raise websockets.exceptions.ConnectionClosed(0, 'websocket closed')
@@ -517,15 +542,15 @@ async def _debug_logger(self):
517542
self.debug_log_shown_lines += number_of_lines_written
518543

519544
if self.debug_log_shown_lines >= self.debug_log_params['limit']:
520-
jasyncio.create_task(self.close())
545+
jasyncio.create_task(self.close(), name="Task_Close")
521546
return
522547

523548
except KeyError as e:
524549
log.exception('Unexpected debug line -- %s' % e)
525-
jasyncio.create_task(self.close())
550+
jasyncio.create_task(self.close(), name="Task_Close")
526551
raise
527552
except jasyncio.CancelledError:
528-
jasyncio.create_task(self.close())
553+
jasyncio.create_task(self.close(), name="Task_Close")
529554
raise
530555
except websockets.exceptions.ConnectionClosed:
531556
log.warning('Debug Logger: Connection closed, reconnecting')
@@ -536,7 +561,7 @@ async def _debug_logger(self):
536561
return
537562
except Exception as e:
538563
log.exception("Error in debug logger : %s" % e)
539-
jasyncio.create_task(self.close())
564+
jasyncio.create_task(self.close(), name="Task_Close")
540565
raise
541566

542567
async def _receiver(self):
@@ -552,7 +577,8 @@ async def _receiver(self):
552577
result = json.loads(result)
553578
await self.messages.put(result['request-id'], result)
554579
except jasyncio.CancelledError:
555-
raise
580+
log.debug('Receiver: Cancelled')
581+
pass
556582
except websockets.exceptions.ConnectionClosed as e:
557583
log.warning('Receiver: Connection closed, reconnecting')
558584
await self.messages.put_all(e)
@@ -592,7 +618,8 @@ async def _do_ping():
592618
break
593619
await jasyncio.sleep(10)
594620
except jasyncio.CancelledError:
595-
raise
621+
log.debug('Pinger: Cancelled')
622+
pass
596623
except websockets.exceptions.ConnectionClosed:
597624
# The connection has closed - we can't do anything
598625
# more until the connection is restarted.
@@ -769,7 +796,7 @@ async def reconnect(self):
769796
if not self.is_debug_log_connection:
770797
self._build_facades(res.get('facades', {}))
771798
if not self._pinger_task:
772-
self._pinger_task = jasyncio.create_task(self._pinger())
799+
self._pinger_task = jasyncio.create_task(self._pinger(), name="Task_Pinger")
773800

774801
async def _connect(self, endpoints):
775802
if len(endpoints) == 0:
@@ -820,12 +847,12 @@ async def _try_endpoint(endpoint, cacert, delay):
820847
# If this is a debug-log connection, and the _debug_log_task
821848
# is not created yet, then go ahead and schedule it
822849
if self.is_debug_log_connection and not self._debug_log_task:
823-
self._debug_log_task = jasyncio.create_task(self._debug_logger())
850+
self._debug_log_task = jasyncio.create_task(self._debug_logger(), name="Task_Debug_Log")
824851

825852
# If this is regular connection, and we dont have a
826853
# receiver_task yet, then schedule a _receiver_task
827854
elif not self.is_debug_log_connection and not self._receiver_task:
828-
self._receiver_task = jasyncio.create_task(self._receiver())
855+
self._receiver_task = jasyncio.create_task(self._receiver(), name="Task_Receiver")
829856

830857
log.debug("Driver connected to juju %s", self.addr)
831858
self.monitor.close_called.clear()
@@ -880,7 +907,7 @@ async def _connect_with_redirect(self, endpoints):
880907
login_result = await self._connect_with_login(e.endpoints)
881908
self._build_facades(login_result.get('facades', {}))
882909
if not self._pinger_task:
883-
self._pinger_task = jasyncio.create_task(self._pinger())
910+
self._pinger_task = jasyncio.create_task(self._pinger(), name="Task_Pinger")
884911

885912
# _build_facades takes the facade list that comes from the connection with the controller,
886913
# validates that the client knows about them (client_facades) and builds the facade list

juju/jasyncio.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
gather, sleep, wait_for, create_subprocess_exec, subprocess, \
2222
wait, FIRST_COMPLETED, Lock, as_completed, new_event_loop, \
2323
get_event_loop_policy, CancelledError, get_running_loop, \
24-
create_task # noqa
24+
create_task, ALL_COMPLETED, all_tasks, current_task, shield # noqa
2525

2626

2727
def create_task_with_handler(coro, task_name, logger=ROOT_LOGGER):

juju/utils.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ async def block_until(*conditions, timeout=None, wait_period=0.5):
128128
async def _block():
129129
while not all(c() for c in conditions):
130130
await jasyncio.sleep(wait_period)
131-
await jasyncio.wait_for(_block(), timeout)
131+
await jasyncio.shield(jasyncio.wait_for(_block(), timeout))
132132

133133

134134
async def block_until_with_coroutine(condition_coroutine, timeout=None, wait_period=0.5):
@@ -139,7 +139,7 @@ async def block_until_with_coroutine(condition_coroutine, timeout=None, wait_per
139139
async def _block():
140140
while not await condition_coroutine():
141141
await jasyncio.sleep(wait_period)
142-
await jasyncio.wait_for(_block(), timeout=timeout)
142+
await jasyncio.shield(jasyncio.wait_for(_block(), timeout=timeout))
143143

144144

145145
async def wait_for_bundle(model, bundle, **kwargs):
@@ -181,6 +181,11 @@ async def run_with_interrupt(task, *events, log=None):
181181
return_when=jasyncio.FIRST_COMPLETED)
182182
for f in pending:
183183
f.cancel() # cancel unfinished tasks
184+
for f in pending:
185+
try:
186+
await f
187+
except jasyncio.CancelledError:
188+
pass
184189
for f in done:
185190
f.exception() # prevent "exception was not retrieved" errors
186191
if task in done:

tests/integration/a.file

Whitespace-only changes.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
options:
2+
status:
3+
type: string
4+
default: "active"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/bash
2+
3+
status="$(config-get status)"
4+
5+
if [[ "$status" == "error" ]]; then
6+
if [[ -e .errored ]]; then
7+
status="active"
8+
else
9+
touch .errored
10+
exit 1
11+
fi
12+
fi
13+
status-set "$status"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
bases:
2+
- architectures:
3+
- amd64
4+
channel: '22.04'
5+
name: ubuntu
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
name: assumes-charm
2+
summary: "test"
3+
description: "test"
4+
maintainers: ["test"]
5+
assumes:
6+
- juju
7+
- any-of:
8+
- all-of:
9+
- juju >= 2.9
10+
- juju < 3
11+
- all-of:
12+
- juju >= 3.1
13+
- juju < 4

tests/integration/test_connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ async def test_monitor(event_loop):
3131
assert conn.monitor.status == 'connected'
3232
await conn.close()
3333

34-
assert conn.monitor.status == 'disconnected'
34+
assert conn.monitor.status == 'disconnecting'
3535

3636

3737
@base.bootstrapped

0 commit comments

Comments
 (0)