Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
F
fuss-manager
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
32
Issues
32
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Packages & Registries
Packages & Registries
Package Registry
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
FUSS
fuss-manager
Commits
74231163
Commit
74231163
authored
Aug 12, 2020
by
Elena Grandi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
flake8
parent
ad2f0586
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
150 additions
and
180 deletions
+150
-180
manager/config.py
manager/config.py
+34
-34
manager/sources/__init__.py
manager/sources/__init__.py
+1
-1
manager/sources/arpwatch.py
manager/sources/arpwatch.py
+10
-11
manager/sources/chaos.py
manager/sources/chaos.py
+2
-3
manager/sources/command.py
manager/sources/command.py
+3
-8
manager/sources/dhcp.py
manager/sources/dhcp.py
+22
-26
manager/sources/inotifywait.py
manager/sources/inotifywait.py
+3
-4
manager/users/master.py
manager/users/master.py
+7
-14
tests/test_config.py
tests/test_config.py
+2
-3
tests/test_ops.py
tests/test_ops.py
+62
-69
tests/test_source_chaos.py
tests/test_source_chaos.py
+4
-7
No files found.
manager/config.py
View file @
74231163
...
...
@@ -5,40 +5,40 @@ import ruamel.yaml
log
=
logging
.
getLogger
(
__name__
)
DEFAULTS
=
{
'path_ansible_inventory'
:
"/etc/fuss-manager/hosts"
,
'path_stats_cache'
:
"/etc/fuss-manager/machine_stats"
,
'path_playbooks'
:
"/etc/fuss-manager/playbooks"
,
'web_address'
:
"localhost"
,
'web_port'
:
1232
,
'arpwatch_datadir'
:
'/var/lib/arpwatch/'
,
'dhcp_log_file'
:
'/var/log/syslog'
,
'debug'
:
False
,
'operation_log_file'
:
'/var/log/fuss-manager/operations.log'
,
'event_log_file'
:
None
,
'playbook_log_dir'
:
'/var/log/fuss-manager/playbooks.log'
,
'disabled_sources'
:
(),
'permission_file'
:
'/etc/fuss-manager/perms.yaml'
,
'master_pass_file'
:
'/etc/fuss-server/fuss-server.yaml'
,
'ldap_uri'
:
False
,
'ldap_user_search_base'
:
False
,
'ldap_group_search_base'
:
False
,
'web_session_dir'
:
None
,
'auth_forward_secret'
:
None
,
}
"path_ansible_inventory"
:
"/etc/fuss-manager/hosts"
,
"path_stats_cache"
:
"/etc/fuss-manager/machine_stats"
,
"path_playbooks"
:
"/etc/fuss-manager/playbooks"
,
"web_address"
:
"localhost"
,
"web_port"
:
1232
,
"arpwatch_datadir"
:
"/var/lib/arpwatch/"
,
"dhcp_log_file"
:
"/var/log/syslog"
,
"debug"
:
False
,
"operation_log_file"
:
"/var/log/fuss-manager/operations.log"
,
"event_log_file"
:
None
,
"playbook_log_dir"
:
"/var/log/fuss-manager/playbooks.log"
,
"disabled_sources"
:
(),
"permission_file"
:
"/etc/fuss-manager/perms.yaml"
,
"master_pass_file"
:
"/etc/fuss-server/fuss-server.yaml"
,
"ldap_uri"
:
False
,
"ldap_user_search_base"
:
False
,
"ldap_group_search_base"
:
False
,
"web_session_dir"
:
None
,
"auth_forward_secret"
:
None
,
}
MOCK_VALUES
=
{
'path_ansible_inventory'
:
"./hosts"
,
'path_stats_cache'
:
"./machine_stats"
,
'path_playbooks'
:
"./tests/data/playbooks"
,
'dhcp_log_file'
:
'/var/log/syslog'
,
'debug'
:
True
,
'operation_log_file'
:
'./operations.log'
,
'playbook_log_dir'
:
'./playbooks.log'
,
'permission_file'
:
'tests/data/perms.yaml'
,
'master_pass_file'
:
False
,
'auth_forward_secret'
:
'mock-auth-forward-secret'
,
}
"path_ansible_inventory"
:
"./hosts"
,
"path_stats_cache"
:
"./machine_stats"
,
"path_playbooks"
:
"./tests/data/playbooks"
,
"dhcp_log_file"
:
"/var/log/syslog"
,
"debug"
:
True
,
"operation_log_file"
:
"./operations.log"
,
"playbook_log_dir"
:
"./playbooks.log"
,
"permission_file"
:
"tests/data/perms.yaml"
,
"master_pass_file"
:
False
,
"auth_forward_secret"
:
"mock-auth-forward-secret"
,
}
class
Config
:
...
...
@@ -66,9 +66,9 @@ class Config:
p
=
{}
self
.
permissions
=
{
'users'
:
p
.
get
(
'users'
,
{}),
'groups'
:
p
.
get
(
'groups'
,
{}),
}
"users"
:
p
.
get
(
"users"
,
{}),
"groups"
:
p
.
get
(
"groups"
,
{}),
}
class
MockConfig
(
Config
):
...
...
manager/sources/__init__.py
View file @
74231163
...
...
@@ -8,4 +8,4 @@ __all__ = (
"ArpwatchDataSource"
,
"DhcpdDataSource"
,
"ChaosDataSource"
,
)
)
manager/sources/arpwatch.py
View file @
74231163
...
...
@@ -10,6 +10,7 @@ from .inotifywait import Inotifywait
class
ArpwatchDataSource
(
MachineDataSourceMixin
,
DataSource
):
"""
"""
def
__init__
(
self
,
config
=
None
,
datafile
=
None
,
**
kw
):
super
().
__init__
(
**
kw
)
if
datafile
:
...
...
@@ -35,15 +36,12 @@ class ArpwatchDataSource(MachineDataSourceMixin, DataSource):
break
elif
event
==
"START"
:
self
.
started
.
set_result
(
True
)
await
self
.
event_hub
.
publish
(
events
.
DataSourceStartEvent
(
self
)
)
await
self
.
event_hub
.
publish
(
events
.
DataSourceStartEvent
(
self
))
await
self
.
event_hub
.
publish
(
events
.
DataSourceStopEvent
(
self
))
async
def
run
(
self
):
await
asyncio
.
gather
(
self
.
watch
.
run
(),
self
.
read_events
(),
self
.
watch
.
run
(),
self
.
read_events
(),
)
@
classmethod
...
...
@@ -63,11 +61,12 @@ class ArpwatchDataSource(MachineDataSourceMixin, DataSource):
mac
=
parts
[
0
]
ip
=
parts
[
1
]
if
self
.
check_ip
(
ip
)
and
self
.
check_mac
(
mac
):
await
self
.
event_hub
.
publish
(
events
.
HostSeenEvent
(
self
,
mac
=
mac
,
ip
=
ip
,
timestamp
=
ts
,
))
await
self
.
event_hub
.
publish
(
events
.
HostSeenEvent
(
self
,
mac
=
mac
,
ip
=
ip
,
timestamp
=
ts
,
)
)
else
:
logging
.
warning
(
"Found unparseable arpwatch line: %s"
,
line
)
"Found unparseable arpwatch line: %s"
,
line
)
manager/sources/chaos.py
View file @
74231163
...
...
@@ -20,6 +20,7 @@ MOCK_NETWORK = [
class
ChaosDataSource
(
DataSource
):
"""
"""
def
__init__
(
self
,
config
=
None
,
**
kw
):
super
().
__init__
(
**
kw
)
loop
=
asyncio
.
get_event_loop
()
...
...
@@ -50,9 +51,7 @@ class ChaosDataSource(DataSource):
self
.
sleep_task
=
None
host
=
random
.
choice
(
MOCK_NETWORK
)
await
self
.
event_hub
.
publish
(
events
.
HostSeenEvent
(
self
,
**
host
)
)
await
self
.
event_hub
.
publish
(
events
.
HostSeenEvent
(
self
,
**
host
))
await
self
.
event_hub
.
publish
(
events
.
DataSourceStopEvent
(
self
))
...
...
manager/sources/command.py
View file @
74231163
...
...
@@ -40,10 +40,9 @@ class CommandDataSource(DataSource):
# the following is useful in case the watched file is in
# python, and does not hurt otherwise.
'env'
:
{
'PYTHONUNBUFFERED'
:
'1'
},
}
}
proc
=
await
asyncio
.
create_subprocess_exec
(
*
self
.
command
,
**
command_kw
*
self
.
command
,
**
command_kw
)
parsers
=
[
...
...
@@ -117,8 +116,4 @@ class CommandDataSource(DataSource):
line
=
await
stream
.
readline
()
if
not
line
:
break
self
.
log
.
warn
(
"{}: {}: {}"
.
format
(
self
.
command
,
label
,
line
,
))
self
.
log
.
warn
(
"{}: {}: {}"
.
format
(
self
.
command
,
label
,
line
,))
manager/sources/dhcp.py
View file @
74231163
...
...
@@ -15,6 +15,7 @@ class DhcpdDataSource(MachineDataSourceMixin, CommandDataSource):
Keep lists of all seen mac and ip addresses, and names.
"""
def
__init__
(
self
,
config
=
None
,
**
kw
):
# TODO: change this with a command to read DCHP lease data
command
=
[
'tail'
,
'-f'
,
config
.
dhcp_log_file
]
...
...
@@ -36,46 +37,41 @@ class DhcpdDataSource(MachineDataSourceMixin, CommandDataSource):
if
message
.
startswith
(
b'DHCPDISCOVER'
):
mac
=
message
.
split
()[
2
].
decode
()
if
self
.
check_mac
(
mac
):
await
self
.
event_hub
.
publish
(
events
.
HostSeenEvent
(
self
,
mac
=
mac
,
timestamp
=
timestamp
,
)
)
await
self
.
event_hub
.
publish
(
events
.
HostSeenEvent
(
self
,
mac
=
mac
,
timestamp
=
timestamp
,
)
)
else
:
logging
.
warning
(
"Received an unparsable DHCPDISCOVER message: %r"
,
line
)
"Received an unparsable DHCPDISCOVER message: %r"
,
line
)
elif
message
.
startswith
(
b'DHCPACK'
):
mac
=
message
.
split
()[
4
].
decode
()
ip
=
message
.
split
()[
2
].
decode
()
if
self
.
check_mac
(
mac
)
and
self
.
check_ip
(
ip
):
await
self
.
event_hub
.
publish
(
events
.
HostSeenEvent
(
self
,
mac
=
mac
,
ip
=
ip
,
timestamp
=
timestamp
,
))
await
self
.
event_hub
.
publish
(
events
.
HostSeenEvent
(
self
,
mac
=
mac
,
ip
=
ip
,
timestamp
=
timestamp
,
)
)
else
:
logging
.
warning
(
"Received an unparsable DHCPACK message: %r"
,
line
)
"Received an unparsable DHCPACK message: %r"
,
line
)
elif
message
.
startswith
(
b'Added new forward map'
):
ip
=
message
.
split
()[
7
].
decode
()
name
=
message
.
split
()[
5
].
decode
()
if
self
.
check_ip
(
ip
)
and
self
.
check_hostname
(
name
):
await
self
.
event_hub
.
publish
(
events
.
HostSeenEvent
(
self
,
ip
=
ip
,
name
=
name
,
timestamp
=
timestamp
,
))
await
self
.
event_hub
.
publish
(
events
.
HostSeenEvent
(
self
,
ip
=
ip
,
name
=
name
,
timestamp
=
timestamp
,
)
)
else
:
logging
.
warning
(
"Received an unparsable forward map message: %r"
,
line
)
"Received an unparsable forward map message: %r"
,
line
)
@
classmethod
def
is_viable
(
cls
,
config
):
...
...
manager/sources/inotifywait.py
View file @
74231163
...
...
@@ -9,6 +9,7 @@ class Inotifywait:
"""
Use inotifywait to wait until a file changes
"""
# The best way here would be to use a python interface to
# inotify: this is a quick and dirty workaround because the
# state of maintainance of python3-pyinotify is somewhat
...
...
@@ -68,10 +69,8 @@ class Inotifywait:
res
=
await
self
.
proc
.
wait
()
if
res
!=
-
signal
.
SIGTERM
:
self
.
log
.
warn
(
"%s exited with error code %d"
,
self
.
path_inotifywait
,
res
)
"%s exited with error code %d"
,
self
.
path_inotifywait
,
res
)
if
self
.
queue
:
await
self
.
queue
.
put
(
"END"
)
...
...
manager/users/master.py
View file @
74231163
...
...
@@ -45,7 +45,7 @@ class Master(db.DB):
if
user
!=
'root'
:
raise
db
.
AuthenticationError
(
"User is not allowed to use the master password"
)
)
try
:
with
open
(
self
.
pass_file
)
as
fp
:
...
...
@@ -53,7 +53,7 @@ class Master(db.DB):
except
PermissionError
:
raise
db
.
AuthenticationError
(
"Master password file is not accessible"
)
)
masterpwd
=
data
.
get
(
'pass'
)
if
not
masterpwd
:
...
...
@@ -64,21 +64,14 @@ class Master(db.DB):
# TODO: should this be some specific value?
uid
=
sum
(
ord
(
x
)
for
x
in
user
)
groups
=
[
db
.
Group
(
name
=
'root'
,
id
=
uid
)
]
groups
=
[
db
.
Group
(
name
=
'root'
,
id
=
uid
)]
display_name
=
'root'
return
db
.
User
(
name
=
user
,
id
=
uid
,
display_name
=
display_name
,
groups
=
groups
)
name
=
user
,
id
=
uid
,
display_name
=
display_name
,
groups
=
groups
)
async
def
authenticate
(
self
,
user
:
str
,
password
:
str
)
->
db
.
User
:
loop
=
asyncio
.
get_running_loop
()
return
await
loop
.
run_in_executor
(
None
,
self
.
_sync_authenticate
,
user
,
password
)
None
,
self
.
_sync_authenticate
,
user
,
password
)
tests/test_config.py
View file @
74231163
...
...
@@ -22,9 +22,8 @@ class TestEventHub(unittest.TestCase):
conf
=
config
.
Config
(
'tests/data/fuss_manager.yaml.override'
)
# One "randomish" value that should have the default value
self
.
assertEqual
(
conf
.
path_ansible_inventory
,
'/etc/fuss-manager/hosts'
)
conf
.
path_ansible_inventory
,
'/etc/fuss-manager/hosts'
)
# And the values that have been changed in the file above
self
.
assertEqual
(
conf
.
web_port
,
8888
)
self
.
assertEqual
(
conf
.
debug
,
True
)
...
...
tests/test_ops.py
View file @
74231163
...
...
@@ -21,12 +21,14 @@ class ManagerTestException(Exception):
self
.
tb_fmt
=
tb_fmt
def
__str__
(
self
):
return
"
\n
"
.
join
((
"{}({})"
.
format
(
self
.
cls
.
__name__
,
repr
(
self
.
value
)),
"----8<---- traceback in manager test ----8<----"
,
textwrap
.
indent
(
self
.
tb_fmt
.
rstrip
(),
" "
),
"----8<---- traceback in manager test ----8<----"
,
))
return
"
\n
"
.
join
(
(
"{}({})"
.
format
(
self
.
cls
.
__name__
,
repr
(
self
.
value
)),
"----8<---- traceback in manager test ----8<----"
,
textwrap
.
indent
(
self
.
tb_fmt
.
rstrip
(),
" "
),
"----8<---- traceback in manager test ----8<----"
,
)
)
def
manager_test
(
testfunc
):
...
...
@@ -48,17 +50,14 @@ def manager_test(testfunc):
exception_formatted
=
traceback
.
format_exc
()
yield
from
self
.
manager
.
shutdown
()
self
.
loop
.
run_until_complete
(
asyncio
.
gather
(
self
.
manager
.
run
(),
test_main
(),
))
self
.
loop
.
run_until_complete
(
asyncio
.
gather
(
self
.
manager
.
run
(),
test_main
(),)
)
if
exception_type
is
not
None
:
raise
ManagerTestException
(
exception_type
,
exception_val
,
exception_formatted
)
exception_type
,
exception_val
,
exception_formatted
)
return
wrap
...
...
@@ -68,17 +67,16 @@ class TestManagerMixin(TestCase):
config
=
Config
()
config
.
operation_log_file
=
os
.
path
.
join
(
self
.
tempdir
.
name
,
"ops.log"
)
config
.
path_ansible_inventory
=
os
.
path
.
join
(
self
.
tempdir
.
name
,
"inventory"
)
self
.
tempdir
.
name
,
"inventory"
)
with
open
(
config
.
path_ansible_inventory
,
'w'
)
as
fp
:
fp
.
write
(
''
)
config
.
path_stats_cache
=
os
.
path
.
join
(
self
.
tempdir
.
name
,
"stats"
)
config
.
disabled_sources
=
(
"ArpwatchDataSource"
,
"DhcpdDataSource"
,
"ChaosDataSource"
)
"ChaosDataSource"
,
)
config
.
debug
=
True
config
.
permission_file
=
'tests/data/perms.yaml'
config
.
_load_perms
()
...
...
@@ -141,11 +139,10 @@ class TestOps(TestManagerMixin, TestCase):
self
.
users
=
{
'admin'
:
User
(
name
=
'admin'
,
id
=
123
),
'user'
:
User
(
name
=
'user'
,
id
=
124
,
groups
=
[
Group
(
name
=
'user'
,
id
=
124
)]),
name
=
'user'
,
id
=
124
,
groups
=
[
Group
(
name
=
'user'
,
id
=
124
)]
),
'none'
:
None
,
}
}
@
manager_test
def
test_noop
(
self
):
...
...
@@ -156,73 +153,71 @@ class TestOps(TestManagerMixin, TestCase):
@
manager_test
def
test_add_del_group
(
self
):
test_mac
=
"11:22:33:44:55:66"
yield
from
self
.
manager
.
event_hub
.
publish
(
events
.
HostSeenEvent
(
None
,
mac
=
test_mac
))
yield
from
self
.
manager
.
event_hub
.
publish
(
events
.
HostSeenEvent
(
None
,
mac
=
test_mac
)
)
for
user
in
[
'admin'
]:
with
self
.
gather_events
()
as
gathered
:
yield
from
self
.
manager
.
execute
(
ops
.
AddGroup
(
mac
=
test_mac
,
group
=
"test"
,
user
=
self
.
users
[
user
],
)
)
yield
from
self
.
manager
.
execute
(
ops
.
AddGroup
(
mac
=
test_mac
,
group
=
"test"
,
user
=
self
.
users
[
user
]
,
)
)
self
.
assertEqual
(
gathered
[
0
].
changes
,
{
'groups'
:
((),
(
'test'
,))})
for
user
in
[
'admin'
]:
with
self
.
gather_events
()
as
gathered
:
yield
from
self
.
manager
.
execute
(
ops
.
DelGroup
(
mac
=
test_mac
,
group
=
"test"
,
user
=
self
.
users
[
user
],
)
)
yield
from
self
.
manager
.
execute
(
ops
.
DelGroup
(
mac
=
test_mac
,
group
=
"test"
,
user
=
self
.
users
[
user
]
,
)
)
self
.
assertEqual
(
gathered
[
0
].
changes
,
{
'groups'
:
((
'test'
,),
())})
for
user
in
[
'user'
,
'none'
]:
with
self
.
gather_events
()
as
gathered
:
with
self
.
assertRaises
(
ops
.
OpPermissionDenied
):
yield
from
self
.
manager
.
execute
(
ops
.
AddGroup
(
mac
=
test_mac
,
group
=
"test"
,
user
=
self
.
users
[
user
],
)
)
yield
from
self
.
manager
.
execute
(
ops
.
AddGroup
(
mac
=
test_mac
,
group
=
"test"
,
user
=
self
.
users
[
user
]
,
)
)
self
.
assertEqual
(
gathered
,
[])
for
user
in
[
'user'
,
'none'
]:
with
self
.
gather_events
()
as
gathered
:
with
self
.
assertRaises
(
ops
.
OpPermissionDenied
):
yield
from
self
.
manager
.
execute
(
ops
.
DelGroup
(
mac
=
test_mac
,
group
=
"test"
,
user
=
self
.
users
[
user
],
)
)
yield
from
self
.
manager
.
execute
(
ops
.
DelGroup
(
mac
=
test_mac
,
group
=
"test"
,
user
=
self
.
users
[
user
]
,
)
)
self
.
assertEqual
(
gathered
,
[])
@
manager_test
def
test_delgroup
(
self
):
test_mac
=
"11:22:33:44:55:66"
yield
from
self
.
manager
.
event_hub
.
publish
(
events
.
HostSeenEvent
(
None
,
mac
=
test_mac
))
yield
from
self
.
manager
.
event_hub
.
publish
(
events
.
HostSeenEvent
(
None
,
mac
=
test_mac
)
)
for
user
in
[
'admin'
]:
with
self
.
gather_events
()
as
gathered
:
yield
from
self
.
manager
.
execute
(
ops
.
DelGroup
(
mac
=
test_mac
,
group
=
"test"
,
user
=
self
.
users
[
user
],
)
)
yield
from
self
.
manager
.
execute
(
ops
.
DelGroup
(
mac
=
test_mac
,
group
=
"test"
,
user
=
self
.
users
[
user
]
,
)
)
self
.
assertEqual
(
gathered
,
[])
for
user
in
[
'user'
,
'none'
]:
with
self
.
gather_events
()
as
gathered
:
with
self
.
assertRaises
(
ops
.
OpPermissionDenied
):
yield
from
self
.
manager
.
execute
(
ops
.
DelGroup
(
mac
=
test_mac
,
group
=
"test"
,
user
=
self
.
users
[
user
],
)
)
yield
from
self
.
manager
.
execute
(
ops
.
DelGroup
(
mac
=
test_mac
,
group
=
"test"
,
user
=
self
.
users
[
user
]
,
)
)
self
.
assertEqual
(
gathered
,
[])
@
manager_test
...
...
@@ -230,17 +225,15 @@ class TestOps(TestManagerMixin, TestCase):
mac
=
"11:22:33:44:55:66"
for
user
in
[
'admin'
]:
with
self
.
gather_events
()
as
gathered
:
yield
from
self
.
manager
.
execute
(
ops
.
WakeOnLan
(
macs
=
[
mac
],
user
=
self
.
users
[
user
],
))
yield
from
self
.
manager
.
execute
(
ops
.
WakeOnLan
(
macs
=
[
mac
],
user
=
self
.
users
[
user
],)
)
self
.
assertEqual
(
gathered
,
[])
for
user
in
[
'user'
,
'none'
]:
with
self
.
gather_events
()
as
gathered
:
with
self
.
assertRaises
(
ops
.
OpPermissionDenied
):
yield
from
self
.
manager
.
execute
(
ops
.
WakeOnLan
(
macs
=
[
mac
],
user
=
self
.
users
[
user
],
))
yield
from
self
.
manager
.
execute
(
ops
.
WakeOnLan
(
macs
=
[
mac
],
user
=
self
.
users
[
user
],)
)
self
.
assertEqual
(
gathered
,
[])
tests/test_source_chaos.py
View file @
74231163
...
...
@@ -8,7 +8,7 @@ from .common import (
TestDataSourceMixin
,
StopTest
,
GatherEvents
,
)
)
from
manager
import
compat
...
...
@@ -16,13 +16,11 @@ class TestChaosDataSource(TestDataSourceMixin, AsyncTestCase):
"""
test a data source that generates sample data.
"""
def
make_datasource
(
self
):
config
=
Config
()
config
.
arpwatch_datafile
=
self
.
tmpfile
.
name
return
ds
.
ChaosDataSource
(
event_hub
=
self
.
event_hub
,
config
=
config
,
)
return
ds
.
ChaosDataSource
(
event_hub
=
self
.
event_hub
,
config
=
config
,)
def
setUp
(
self
,
*
args
,
**
kw
):
self
.
tmpfile
=
tempfile
.
NamedTemporaryFile
()
...
...
@@ -45,8 +43,7 @@ class TestChaosDataSource(TestDataSourceMixin, AsyncTestCase):
yield
from
self
.
event_hub
.
publish
(
StopTest
(
None
))
await
asyncio
.
gather
(
compat
.
create_task
(
self
.
ds
.
run
()),
compat
.
create_task
(
test
()),
compat
.
create_task
(
self
.
ds
.
run
()),
compat
.
create_task
(
test
()),
)
events
=
events
.
gathered
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment