Skip to content

Commit 91194f0

Browse files
authored
Merge pull request #422 from hpidcock/various-fixes
#422 - Fix positional argument usage in facade calls. - Add get shim to facade types. - Fix SSH await on unit - Fix integration tests Fixes #420
2 parents 49c5554 + 11e3e81 commit 91194f0

9 files changed

Lines changed: 102 additions & 90 deletions

File tree

.github/workflows/tox.yaml

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,17 @@
1-
name: Run tests with Tox
1+
name: Unit Tests
22

3-
on: [push]
3+
on: [push, pull_request]
44

55
jobs:
6-
build:
7-
6+
unit-tests:
87
runs-on: ubuntu-latest
9-
strategy:
10-
matrix:
11-
python: [3.6, 3.7, 3.8]
12-
juju: [stable, edge]
13-
148
steps:
159
- uses: actions/checkout@v2
1610
- name: Setup Python
1711
uses: actions/setup-python@v1
1812
with:
19-
python-version: ${{ matrix.python }}
13+
python-version: 3.8
2014
- name: Install Tox and any other packages
2115
run: pip install tox
2216
- name: Run Lint and Unit Tests
2317
run: tox -e lint,py3
24-
- name: Install Snaps
25-
run: |
26-
sudo snap install lxd
27-
sudo snap install juju --classic --channel ${{ matrix.juju }}
28-
sudo snap install juju-wait --classic
29-
- name: Configure LXD
30-
run: |
31-
sudo lxd waitready --timeout 30
32-
sudo chmod 666 /var/snap/lxd/common/lxd/unix.socket
33-
lxd init --auto --network-address='[::]' --network-port=8443 --storage-backend=dir
34-
lxc network set lxdbr0 ipv6.address none
35-
- name: Bootstrap Juju Controller
36-
run: juju bootstrap localhost test --config 'identity-url=https://api.staging.jujucharms.com/identity' --config 'allow-model-access=true' --config 'test-mode=true'
37-
- name: Run Integration Tests
38-
run: tox -e integration,serial
39-
env:
40-
TEST_AGENTS: '{"agents":[{"url":"https://api.staging.jujucharms.com/identity","username":"libjuju-ci@yellow"}],"key":{"private":"88OOCxIHQNguRG7zFg2y2Hx5Ob0SeVKKBRnjyehverc=","public":"fDn20+5FGyN2hYO7z0rFUyoHGUnfrleslUNtoYsjNSs="}}'

juju/client/facade.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,16 @@ def __setitem__(self, key, value):
654654
attr = self._toPy[key]
655655
setattr(self, attr, value)
656656

657+
# legacy: generated definitions used to not correctly
658+
# create typed objects and would use dict instead (from JSON)
659+
# so we emulate some dict methods.
660+
def get(self, key, default=None):
661+
try:
662+
attr = self._toPy[key]
663+
except KeyError:
664+
return default
665+
return getattr(self, attr, default)
666+
657667

658668
class Schema(dict):
659669
def __init__(self, schema):

juju/controller.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ async def reset_user_password(self, username):
447447
user_facade = client.UserManagerFacade.from_connection(
448448
self.connection())
449449
entity = client.Entity(tag.user(username))
450-
results = await user_facade.ResetPassword([entity])
450+
results = await user_facade.ResetPassword(entities=[entity])
451451
secret_key = results.results[0].secret_key
452452
return await self.get_user(username, secret_key=secret_key)
453453

@@ -755,7 +755,7 @@ async def create_offer(self, model_uuid, endpoint, offer_name=None, application_
755755
params.model_tag = tag.model(model_uuid)
756756

757757
facade = client.ApplicationOffersFacade.from_connection(self.connection())
758-
return await facade.Offer([params])
758+
return await facade.Offer(offers=[params])
759759

760760
async def list_offers(self, model_name):
761761
"""
@@ -766,7 +766,7 @@ async def list_offers(self, model_name):
766766
params.model_name = model_name
767767

768768
facade = client.ApplicationOffersFacade.from_connection(self.connection())
769-
return await facade.ListApplicationOffers([params])
769+
return await facade.ListApplicationOffers(filters=[params])
770770

771771
async def remove_offer(self, model_uuid, offer, force=False):
772772
"""
@@ -791,15 +791,15 @@ async def remove_offer(self, model_uuid, offer, force=False):
791791
raise RemoveError("removing offer will also remove relations, use force and try again.")
792792

793793
facade = client.ApplicationOffersFacade.from_connection(self.connection())
794-
return await facade.DestroyOffers(force, [url.string()])
794+
return await facade.DestroyOffers(force=force, offer_urls=[url.string()])
795795

796796
async def get_consume_details(self, endpoint):
797797
"""
798798
get_consume_details returns the details necessary to pass to another
799799
model to consume the specified offers represented by the urls.
800800
"""
801801
facade = client.ApplicationOffersFacade.from_connection(self.connection())
802-
offers = await facade.GetConsumeDetails([endpoint])
802+
offers = await facade.GetConsumeDetails(offer_urls=[endpoint])
803803
if len(offers.results) != 1:
804804
raise JujuAPIError("expected to find one result")
805805
result = offers.results[0]

juju/machine.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,8 @@ async def ssh(
215215
'-q',
216216
destination
217217
]
218-
cmd.extend(ssh_opts.split() if isinstance(ssh_opts, str) else ssh_opts)
218+
if ssh_opts:
219+
cmd.extend(ssh_opts.split() if isinstance(ssh_opts, str) else ssh_opts)
219220
cmd.extend([command])
220221
loop = self.model.loop
221222
process = await asyncio.create_subprocess_exec(*cmd, loop=loop)

juju/unit.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ async def destroy(self):
9999
log.debug(
100100
'Destroying %s', self.name)
101101

102-
return await app_facade.DestroyUnits([self.name])
102+
return await app_facade.DestroyUnits(unit_names=[self.name])
103103
remove = destroy
104104

105105
def get_resources(self, details=False):
@@ -222,7 +222,7 @@ def set_meter_status(self):
222222
"""
223223
raise NotImplementedError()
224224

225-
def ssh(
225+
async def ssh(
226226
self, command, user=None, proxy=False, ssh_opts=None):
227227
"""Execute a command over SSH on this unit.
228228
@@ -232,7 +232,7 @@ def ssh(
232232
:param str ssh_opts: Additional options to the `ssh` command
233233
234234
"""
235-
self.machine.ssh(command, user, proxy, ssh_opts)
235+
return await self.machine.ssh(command, user, proxy, ssh_opts)
236236

237237
def status_history(self, num=20, utc=False):
238238
"""Get status history for this unit.

tests/integration/test_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ async def test_user_info(event_loop):
1313

1414
um = client.UserManagerFacade.from_connection(controller_conn)
1515
result = await um.UserInfo(
16-
[client.Entity('user-admin')], True)
16+
entities=[client.Entity('user-admin')], include_disabled=True)
1717
await controller_conn.close()
1818

1919
assert isinstance(result, client.UserInfoResults)

tests/integration/test_connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ async def test_full_status(event_loop):
6666

6767
c = client.ClientFacade.from_connection(model.connection())
6868

69-
await c.FullStatus(None)
69+
await c.FullStatus(patterns=None)
7070

7171

7272
@base.bootstrapped

tests/integration/test_errors.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ async def test_juju_api_error(event_loop):
1818

1919
async with base.CleanModel() as model:
2020
with pytest.raises(JujuAPIError):
21-
await model.add_machine(constraints={'mem': 'foo'})
21+
await model.add_machine(constraints={'mem': -50})
2222

2323

2424
@base.bootstrapped
@@ -65,4 +65,4 @@ async def test_juju_error_in_result(event_loop):
6565
app_facade = client.ApplicationFacade.from_connection(model.connection())
6666

6767
with pytest.raises(JujuError):
68-
return await app_facade.GetCharmURL('foo')
68+
return await app_facade.GetCharmURL(application='foo')

tests/unit/test_definitions.py

Lines changed: 74 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,69 +4,93 @@
44

55

66
class TestDefinitions(unittest.TestCase):
7+
8+
def test_dict_legacy(self):
9+
status = client.FullStatus.from_json({
10+
'relations': [{
11+
'endpoints': [{
12+
'application': 'application',
13+
'name': 'name',
14+
'role': 'role',
15+
'subordinate': True
16+
}],
17+
'id': 1,
18+
'interface': 'interface',
19+
'key': 'key',
20+
'scope': 'scope',
21+
'status': []
22+
}]
23+
})
24+
if status.relations != status['relations']:
25+
raise Exception('subscript not equal to attr')
26+
if status.relations != status.get('relations'):
27+
raise Exception('get() not equal to attr')
28+
if status.get('non-existing', 'foo') != 'foo':
29+
raise Exception('get defaulting missing attr')
30+
731
def test_parse(self):
832
status = client.FullStatus.from_json({
9-
"relations": [{
10-
"endpoints": [{
11-
"application": "application",
12-
"name": "name",
13-
"role": "role",
14-
"subordinate": True
33+
'relations': [{
34+
'endpoints': [{
35+
'application': 'application',
36+
'name': 'name',
37+
'role': 'role',
38+
'subordinate': True
1539
}],
16-
"id": 1,
17-
"interface": "interface",
18-
"key": "key",
19-
"scope": "scope",
20-
"status": []
40+
'id': 1,
41+
'interface': 'interface',
42+
'key': 'key',
43+
'scope': 'scope',
44+
'status': []
2145
}],
22-
"applications": {
23-
"app": {
24-
"can-upgrade-to": "something",
25-
"charm": "charm",
26-
"charm-profile": "profile",
27-
"charm-version": "2",
28-
"endpoint-bindings": None,
29-
"err": None,
30-
"exposed": True,
31-
"int": 0,
32-
"life": "life",
33-
"meter-statuses": {},
34-
"provider-id": "provider-id",
35-
"public-address": "1.1.1.1",
36-
"relations": {
37-
"a": ["b", "c"],
46+
'applications': {
47+
'app': {
48+
'can-upgrade-to': 'something',
49+
'charm': 'charm',
50+
'charm-profile': 'profile',
51+
'charm-version': '2',
52+
'endpoint-bindings': None,
53+
'err': None,
54+
'exposed': True,
55+
'int': 0,
56+
'life': 'life',
57+
'meter-statuses': {},
58+
'provider-id': 'provider-id',
59+
'public-address': '1.1.1.1',
60+
'relations': {
61+
'a': ['b', 'c'],
3862
},
39-
"series": "focal",
40-
"status": {},
41-
"subordinate-to": ["other"],
42-
"units": {
43-
"unit-id": {
44-
"address": "1.1.1.1",
45-
"agent-status": {},
46-
"charm": "charm",
47-
"leader": True,
48-
"machine": "machine-0",
49-
"opened-ports": ["1234"],
50-
"provider-id": "provider",
51-
"public-address": "1.1.1.2",
52-
"subordinates": {},
53-
"workload-status": {},
54-
"workload-version": "1.2"
63+
'series': 'focal',
64+
'status': {},
65+
'subordinate-to': ['other'],
66+
'units': {
67+
'unit-id': {
68+
'address': '1.1.1.1',
69+
'agent-status': {},
70+
'charm': 'charm',
71+
'leader': True,
72+
'machine': 'machine-0',
73+
'opened-ports': ['1234'],
74+
'provider-id': 'provider',
75+
'public-address': '1.1.1.2',
76+
'subordinates': {},
77+
'workload-status': {},
78+
'workload-version': '1.2'
5579
}
5680
},
57-
"workload-version": "1.2"
81+
'workload-version': '1.2'
5882
}
5983
}
6084
})
6185
if status.relations != status['relations']:
62-
raise Exception("subscript not equal to attr")
86+
raise Exception('subscript not equal to attr')
6387
if status['relations'][0]['endpoints'][0]['application'] != 'application':
64-
raise Exception("failed to use complex subscript")
88+
raise Exception('failed to use complex subscript')
6589
if status.applications['app'].relations['a'][0] != 'b':
66-
raise Exception("object with array type is invalid")
90+
raise Exception('object with array type is invalid')
6791
if not isinstance(status.relations[0], client.RelationStatus):
68-
raise Exception("status relation is not a RelationStatus")
92+
raise Exception('status relation is not a RelationStatus')
6993
if not isinstance(status.relations[0].endpoints[0], client.EndpointStatus):
70-
raise Exception("status relation endpoint is not a EndpointStatus")
94+
raise Exception('status relation endpoint is not a EndpointStatus')
7195
if not isinstance(status.applications['app'], client.ApplicationStatus):
72-
raise Exception("status application is not a ApplicationStatus")
96+
raise Exception('status application is not a ApplicationStatus')

0 commit comments

Comments
 (0)