New N2VC interface + updated libjuju

This commit introduces the Python3 N2VC module, which acts as a standard
interface to the VCA.

The goal of this is to provide a common way for modules to interface
with the VCA.

- Updated libjuju from 0.6.1 to 0.7.3

Signed-off-by: Adam Israel <adam.israel@canonical.com>
Change-Id: Ide70fb5ae5797eb6486de24653dc09a23f9c009e
diff --git a/modules/libjuju/tests/unit/test_client.py b/modules/libjuju/tests/unit/test_client.py
index e9fde8e..42134df 100644
--- a/modules/libjuju/tests/unit/test_client.py
+++ b/modules/libjuju/tests/unit/test_client.py
@@ -5,7 +5,6 @@
 
 import mock
 
-
 from juju.client import client
 
 
diff --git a/modules/libjuju/tests/unit/test_connection.py b/modules/libjuju/tests/unit/test_connection.py
index f69b8d6..0925d84 100644
--- a/modules/libjuju/tests/unit/test_connection.py
+++ b/modules/libjuju/tests/unit/test_connection.py
@@ -1,13 +1,14 @@
 import asyncio
 import json
-import mock
-import pytest
 from collections import deque
 
+import mock
+from juju.client.connection import Connection
 from websockets.exceptions import ConnectionClosed
 
+import pytest
+
 from .. import base
-from juju.client.connection import Connection
 
 
 class WebsocketMock:
@@ -31,7 +32,6 @@
 
 @pytest.mark.asyncio
 async def test_out_of_order(event_loop):
-    con = Connection(*[None]*4)
     ws = WebsocketMock([
         {'request-id': 1},
         {'request-id': 3},
@@ -42,13 +42,24 @@
         {'request-id': 2},
         {'request-id': 3},
     ]
-    con._get_sll = mock.MagicMock()
+    minimal_facades = [{'name': 'Pinger', 'versions': [1]}]
+    con = None
     try:
-        with mock.patch('websockets.connect', base.AsyncMock(return_value=ws)):
-            await con.open()
+        with \
+                mock.patch('websockets.connect', base.AsyncMock(return_value=ws)), \
+                mock.patch(
+                    'juju.client.connection.Connection.login',
+                    base.AsyncMock(return_value={'response': {
+                        'facades': minimal_facades,
+                    }}),
+                ), \
+                mock.patch('juju.client.connection.Connection._get_ssl'), \
+                mock.patch('juju.client.connection.Connection._pinger', base.AsyncMock()):
+            con = await Connection.connect('0.1.2.3:999')
         actual_responses = []
         for i in range(3):
             actual_responses.append(await con.rpc({'version': 1}))
         assert actual_responses == expected_responses
     finally:
-        await con.close()
+        if con:
+            await con.close()
diff --git a/modules/libjuju/tests/unit/test_constraints.py b/modules/libjuju/tests/unit/test_constraints.py
index cb9d773..00b9156 100644
--- a/modules/libjuju/tests/unit/test_constraints.py
+++ b/modules/libjuju/tests/unit/test_constraints.py
@@ -6,6 +6,7 @@
 
 from juju import constraints
 
+
 class TestConstraints(unittest.TestCase):
 
     def test_mem_regex(self):
diff --git a/modules/libjuju/tests/unit/test_loop.py b/modules/libjuju/tests/unit/test_loop.py
index f12368e..9043df6 100644
--- a/modules/libjuju/tests/unit/test_loop.py
+++ b/modules/libjuju/tests/unit/test_loop.py
@@ -1,5 +1,6 @@
 import asyncio
 import unittest
+
 import juju.loop
 
 
@@ -15,6 +16,7 @@
 
     def test_run(self):
         assert asyncio.get_event_loop() == self.loop
+
         async def _test():
             return 'success'
         self.assertEqual(juju.loop.run(_test()), 'success')
diff --git a/modules/libjuju/tests/unit/test_model.py b/modules/libjuju/tests/unit/test_model.py
index 222d881..2e33236 100644
--- a/modules/libjuju/tests/unit/test_model.py
+++ b/modules/libjuju/tests/unit/test_model.py
@@ -1,8 +1,11 @@
 import unittest
 
 import mock
+
 import asynctest
 
+from juju.client.jujudata import FileJujuData
+
 
 def _make_delta(entity, type_, data=None):
     from juju.client.client import Delta
@@ -68,8 +71,8 @@
         from juju.model import Model
         from juju.application import Application
 
-        loop = mock.MagicMock()
-        model = Model(loop=loop)
+        model = Model()
+        model._connector = mock.MagicMock()
         delta = _make_delta('application', 'add', dict(name='foo'))
 
         # test add
@@ -118,7 +121,7 @@
 
 class TestContextManager(asynctest.TestCase):
     @asynctest.patch('juju.model.Model.disconnect')
-    @asynctest.patch('juju.model.Model.connect_current')
+    @asynctest.patch('juju.model.Model.connect')
     async def test_normal_use(self, mock_connect, mock_disconnect):
         from juju.model import Model
 
@@ -129,7 +132,7 @@
         self.assertTrue(mock_disconnect.called)
 
     @asynctest.patch('juju.model.Model.disconnect')
-    @asynctest.patch('juju.model.Model.connect_current')
+    @asynctest.patch('juju.model.Model.connect')
     async def test_exception(self, mock_connect, mock_disconnect):
         from juju.model import Model
 
@@ -143,13 +146,57 @@
         self.assertTrue(mock_connect.called)
         self.assertTrue(mock_disconnect.called)
 
-    @asynctest.patch('juju.client.connection.JujuData.current_controller')
-    async def test_no_current_connection(self, mock_current_controller):
+    async def test_no_current_connection(self):
         from juju.model import Model
         from juju.errors import JujuConnectionError
 
-        mock_current_controller.return_value = ""
+        class NoControllerJujuData(FileJujuData):
+            def current_controller(self):
+                return ""
 
         with self.assertRaises(JujuConnectionError):
-            async with Model():
+            async with Model(jujudata=NoControllerJujuData()):
                 pass
+
+
+class TestModelConnect(asynctest.TestCase):
+    @asynctest.patch('juju.client.connector.Connector.connect_model')
+    @asynctest.patch('juju.model.Model._after_connect')
+    async def test_model_connect_no_args(self, mock_after_connect, mock_connect_model):
+        from juju.model import Model
+        m = Model()
+        await m.connect()
+        mock_connect_model.assert_called_once_with(None)
+
+    @asynctest.patch('juju.client.connector.Connector.connect_model')
+    @asynctest.patch('juju.model.Model._after_connect')
+    async def test_model_connect_with_model_name(self, mock_after_connect, mock_connect_model):
+        from juju.model import Model
+        m = Model()
+        await m.connect(model_name='foo')
+        mock_connect_model.assert_called_once_with('foo')
+
+    @asynctest.patch('juju.client.connector.Connector.connect_model')
+    @asynctest.patch('juju.model.Model._after_connect')
+    async def test_model_connect_with_endpoint_but_no_uuid(
+        self,
+        mock_after_connect,
+        mock_connect_model,
+    ):
+        from juju.model import Model
+        m = Model()
+        with self.assertRaises(ValueError):
+            await m.connect(endpoint='0.1.2.3:4566')
+        self.assertEqual(mock_connect_model.call_count, 0)
+
+    @asynctest.patch('juju.client.connector.Connector.connect')
+    @asynctest.patch('juju.model.Model._after_connect')
+    async def test_model_connect_with_endpoint_and_uuid(
+        self,
+        mock_after_connect,
+        mock_connect,
+    ):
+        from juju.model import Model
+        m = Model()
+        await m.connect(endpoint='0.1.2.3:4566', uuid='some-uuid')
+        mock_connect.assert_called_once_with(endpoint='0.1.2.3:4566', uuid='some-uuid')
diff --git a/modules/libjuju/tests/unit/test_overrides.py b/modules/libjuju/tests/unit/test_overrides.py
index 6485408..a5835ff 100644
--- a/modules/libjuju/tests/unit/test_overrides.py
+++ b/modules/libjuju/tests/unit/test_overrides.py
@@ -1,6 +1,6 @@
-import pytest
+from juju.client.overrides import Binary, Number  # noqa
 
-from juju.client.overrides import Number, Binary  # noqa
+import pytest
 
 
 # test cases ported from:
diff --git a/modules/libjuju/tests/unit/test_placement.py b/modules/libjuju/tests/unit/test_placement.py
index a78a28d..5a933ec 100644
--- a/modules/libjuju/tests/unit/test_placement.py
+++ b/modules/libjuju/tests/unit/test_placement.py
@@ -5,7 +5,7 @@
 import unittest
 
 from juju import placement
-from juju.client import client
+
 
 class TestPlacement(unittest.TestCase):