Repository: juju/python-libjuju

Issue:
AttributeError: 'ExposeChange' object has no attribute 'exposed_endpoints'
This error occurred while using `pytests-operator` + juju==2.9.0, which build the tested charm and tried to deploy it inside bundle.

Environment:
```bash
platform linux -- Python 3.9.5, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
plugins: asyncio-0.15.1, operator-0.6.1
```
How to reproduce:
 * run `tox -e integration` on top of this [commit](https://github.com/r-f-g/charm-openstack-integrator/commit/2a668bc4abb97dffb668d31efb1e9b7cf01c4ef2)

Error:
```bash
_________________________________________________________________________________________________________________________________ test_build_and_deploy __________________________________________________________________________________________________________________________________
Traceback (most recent call last):
  File "/home/rg/canonical/charm-openstack-integrator/tests/integration/test_openstack_integrator_integration.py", line 16, in test_build_and_deploy
    await ops_test.model.deploy(bundle, trust=True)
  File "/home/rg/canonical/charm-openstack-integrator/.tox/integration/lib/python3.9/site-packages/juju/model.py", line 1446, in deploy
    await handler.execute_plan()
  File "/home/rg/canonical/charm-openstack-integrator/.tox/integration/lib/python3.9/site-packages/juju/bundle.py", line 167, in execute_plan 
    self.references[step.id_] = await change.run(self)
  File "/home/rg/canonical/charm-openstack-integrator/.tox/integration/lib/python3.9/site-packages/juju/bundle.py", line 825, in run
    return await context.model.applications[application].expose(self.exposed_endpoints)
AttributeError: 'ExposeChange' object has no attribute 'exposed_endpoints'
================================================================================================================================ short test summary info =================================================================================================================================
FAILED tests/integration/test_openstack_integrator_integration.py::test_build_and_deploy - AttributeError: 'ExposeChange' object has no attribute 'exposed_endpoints'
```

Discussion:

Patch:
diff --git a/juju/bundle.py b/juju/bundle.py
index a54258f..04bc2fb 100644
--- a/juju/bundle.py
+++ b/juju/bundle.py
@@ -800,6 +800,7 @@ class ExposeChange(ChangeInfo):
     def __init__(self, change_id, requires, params=None):
         super(ExposeChange, self).__init__(change_id, requires)
 
+        self.exposed_endpoints = None
         if isinstance(params, list):
             self.application = params[0]
         elif isinstance(params, dict):
diff --git a/tox.ini b/tox.ini
index e2a9b5a..d47696b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -31,6 +31,7 @@ deps =
     pytest-xdist
     Twine
     websockets
+    kubernetes
     # use fork to pick up fix for https://github.com/aaugustin/websockets/pull/528
     git+https://github.com/johnsca/websockets@bug/client-redirects#egg=websockets ; python_version<'3.9'
 


Test patch:
diff --git a/tests/unit/test_bundle.py b/tests/unit/test_bundle.py
index 568ead2..619197b 100644
--- a/tests/unit/test_bundle.py
+++ b/tests/unit/test_bundle.py
@@ -621,7 +621,8 @@ class TestExposeChange(unittest.TestCase):
         change = ExposeChange(1, [], params=["application"])
         self.assertEqual({"change_id": 1,
                           "requires": [],
-                          "application": "application"}, change.__dict__)
+                          "application": "application",
+                          "exposed_endpoints": None}, change.__dict__)
 
     def test_dict_params(self):
         change = ExposeChange(1, [], params={"application": "application"})