Bug 666: Fix window_size overflow with Paramiko
authorAdam Israel <adam.israel@canonical.com>
Fri, 5 Apr 2019 14:17:25 +0000 (10:17 -0400)
committerAdam Israel <adam.israel@canonical.com>
Fri, 5 Apr 2019 14:23:45 +0000 (10:23 -0400)
This commit fixes integration testing around bug fixes in the sshproxy
layer that were causing a window_size overflow.

This also improves reliability around testing machine charms, by
verifying the sshd service is running inside the target machine (lxd)
before running tests.

Change-Id: I465d51521bf87b8e4b3dc5cac07c163fac836393
Signed-off-by: Adam Israel <adam.israel@canonical.com>
13 files changed:
tests/base.py
tests/charms/layers/broken/tests/00-setup [deleted file]
tests/charms/layers/broken/tests/10-deploy [deleted file]
tests/charms/layers/metrics-ci/tests/00-setup [deleted file]
tests/charms/layers/metrics-ci/tests/10-deploy [deleted file]
tests/charms/layers/metrics-proxy-ci/tests/00-setup [deleted file]
tests/charms/layers/metrics-proxy-ci/tests/10-deploy [deleted file]
tests/charms/layers/simple/tests/00-setup [deleted file]
tests/charms/layers/simple/tests/10-deploy [deleted file]
tests/integration/test_charm_native.py
tests/integration/test_metrics_proxy.py
tests/integration/test_non_string_parameter.py
tox.ini

index 3ae5f4f..a0a2b78 100644 (file)
@@ -230,6 +230,25 @@ def create_lxd_container(public_key=None, name="test_name"):
             )
         )
 
+    try:
+        waitcount = 0
+        while waitcount <= 5:
+            if is_sshd_running(container):
+                break
+            waitcount += 1
+            time.sleep(1)
+        if waitcount >= 5:
+            debug("couldn't detect sshd running")
+            raise Exception("Unable to verify container sshd")
+
+    except Exception as ex:
+        debug(
+            "Error checking sshd status on {}: {}".format(
+                test_machine,
+                ex,
+            )
+        )
+
     # HACK: We need to give sshd a chance to bind to the interface,
     # and pylxd's container.execute seems to be broken and fails and/or
     # hangs trying to properly check if the service is up.
@@ -246,6 +265,28 @@ def create_lxd_container(public_key=None, name="test_name"):
     return container
 
 
+def is_sshd_running(container):
+    """Check if sshd is running in the container.
+
+    Check to see if the sshd process is running and listening on port 22.
+
+    :param container: The container to check
+    :return boolean: True if sshd is running.
+    """
+    debug("Container: {}".format(container))
+    try:
+        (rc, stdout, stderr) = container.execute(
+            ["service", "ssh", "status"]
+        )
+        # If the status is a) found and b) running, the exit code will be 0
+        if rc == 0:
+            return True
+    except Exception as ex:
+        debug("Failed to check sshd service status: {}".format(ex))
+
+    return False
+
+
 def destroy_lxd_container(container):
     """Stop and delete a LXD container.
 
@@ -893,11 +934,20 @@ class TestN2VC(object):
             self._running = False
             self._stopping = True
 
+            # Destroy the network service
+            try:
+                await self.n2vc.DestroyNetworkService(self.ns_name)
+            except Exception as e:
+                debug(
+                    "Error Destroying Network Service \"{}\": {}".format(
+                        self.ns_name,
+                        e,
+                    )
+                )
+
+            # Wait for the applications to be removed and delete the containers
             for application in self.charms:
                 try:
-                    await self.n2vc.RemoveCharms(self.ns_name, application)
-
-                    await self.n2vc.DestroyNetworkService(self.ns_name)
 
                     while True:
                         # Wait for the application to be removed
@@ -907,7 +957,6 @@ class TestN2VC(object):
                             application,
                         ):
                             break
-                    await self.n2vc.DestroyNetworkService(self.ns_name)
 
                     # Need to wait for the charm to finish, because native charms
                     if self.state[application]['container']:
@@ -1119,6 +1168,14 @@ class TestN2VC(object):
             debug("{} is done".format(application))
             return
 
+        if status in ['error']:
+            # To test broken charms, if a charm enters an error state we should
+            # end the test
+            debug("{} is in an error state, stop the test.".format(application))
+            # asyncio.ensure_future(self.stop())
+            self.state[application]['done'] = True
+            assert False
+
         if status in ["blocked"] and self.isproxy(application):
             if self.state[application]['phase'] == "deploy":
                 debug("Configuring proxy charm for {}".format(application))
diff --git a/tests/charms/layers/broken/tests/00-setup b/tests/charms/layers/broken/tests/00-setup
deleted file mode 100644 (file)
index f0616a5..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-sudo add-apt-repository ppa:juju/stable -y
-sudo apt-get update
-sudo apt-get install amulet python-requests -y
diff --git a/tests/charms/layers/broken/tests/10-deploy b/tests/charms/layers/broken/tests/10-deploy
deleted file mode 100644 (file)
index 9a26117..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/python3
-
-import amulet
-import requests
-import unittest
-
-
-class TestCharm(unittest.TestCase):
-    def setUp(self):
-        self.d = amulet.Deployment()
-
-        self.d.add('simple')
-        self.d.expose('simple')
-
-        self.d.setup(timeout=900)
-        self.d.sentry.wait()
-
-        self.unit = self.d.sentry['simple'][0]
-
-    def test_service(self):
-        # test we can access over http
-        page = requests.get('http://{}'.format(self.unit.info['public-address']))
-        self.assertEqual(page.status_code, 200)
-        # Now you can use self.d.sentry[SERVICE][UNIT] to address each of the units and perform
-        # more in-depth steps. Each self.d.sentry[SERVICE][UNIT] has the following methods:
-        # - .info - An array of the information of that unit from Juju
-        # - .file(PATH) - Get the details of a file on that unit
-        # - .file_contents(PATH) - Get plain text output of PATH file from that unit
-        # - .directory(PATH) - Get details of directory
-        # - .directory_contents(PATH) - List files and folders in PATH on that unit
-        # - .relation(relation, service:rel) - Get relation data from return service
-
-        
-if __name__ == '__main__':
-    unittest.main()
diff --git a/tests/charms/layers/metrics-ci/tests/00-setup b/tests/charms/layers/metrics-ci/tests/00-setup
deleted file mode 100755 (executable)
index f0616a5..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-sudo add-apt-repository ppa:juju/stable -y
-sudo apt-get update
-sudo apt-get install amulet python-requests -y
diff --git a/tests/charms/layers/metrics-ci/tests/10-deploy b/tests/charms/layers/metrics-ci/tests/10-deploy
deleted file mode 100755 (executable)
index 7595ecf..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/python3
-
-import amulet
-import requests
-import unittest
-
-
-class TestCharm(unittest.TestCase):
-    def setUp(self):
-        self.d = amulet.Deployment()
-
-        self.d.add('metrics-demo')
-        self.d.expose('metrics-demo')
-
-        self.d.setup(timeout=900)
-        self.d.sentry.wait()
-
-        self.unit = self.d.sentry['metrics-demo'][0]
-
-    def test_service(self):
-        # test we can access over http
-        page = requests.get('http://{}'.format(self.unit.info['public-address']))
-        self.assertEqual(page.status_code, 200)
-        # Now you can use self.d.sentry[SERVICE][UNIT] to address each of the units and perform
-        # more in-depth steps. Each self.d.sentry[SERVICE][UNIT] has the following methods:
-        # - .info - An array of the information of that unit from Juju
-        # - .file(PATH) - Get the details of a file on that unit
-        # - .file_contents(PATH) - Get plain text output of PATH file from that unit
-        # - .directory(PATH) - Get details of directory
-        # - .directory_contents(PATH) - List files and folders in PATH on that unit
-        # - .relation(relation, service:rel) - Get relation data from return service
-
-        
-if __name__ == '__main__':
-    unittest.main()
diff --git a/tests/charms/layers/metrics-proxy-ci/tests/00-setup b/tests/charms/layers/metrics-proxy-ci/tests/00-setup
deleted file mode 100644 (file)
index f0616a5..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-sudo add-apt-repository ppa:juju/stable -y
-sudo apt-get update
-sudo apt-get install amulet python-requests -y
diff --git a/tests/charms/layers/metrics-proxy-ci/tests/10-deploy b/tests/charms/layers/metrics-proxy-ci/tests/10-deploy
deleted file mode 100644 (file)
index 7595ecf..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/python3
-
-import amulet
-import requests
-import unittest
-
-
-class TestCharm(unittest.TestCase):
-    def setUp(self):
-        self.d = amulet.Deployment()
-
-        self.d.add('metrics-demo')
-        self.d.expose('metrics-demo')
-
-        self.d.setup(timeout=900)
-        self.d.sentry.wait()
-
-        self.unit = self.d.sentry['metrics-demo'][0]
-
-    def test_service(self):
-        # test we can access over http
-        page = requests.get('http://{}'.format(self.unit.info['public-address']))
-        self.assertEqual(page.status_code, 200)
-        # Now you can use self.d.sentry[SERVICE][UNIT] to address each of the units and perform
-        # more in-depth steps. Each self.d.sentry[SERVICE][UNIT] has the following methods:
-        # - .info - An array of the information of that unit from Juju
-        # - .file(PATH) - Get the details of a file on that unit
-        # - .file_contents(PATH) - Get plain text output of PATH file from that unit
-        # - .directory(PATH) - Get details of directory
-        # - .directory_contents(PATH) - List files and folders in PATH on that unit
-        # - .relation(relation, service:rel) - Get relation data from return service
-
-        
-if __name__ == '__main__':
-    unittest.main()
diff --git a/tests/charms/layers/simple/tests/00-setup b/tests/charms/layers/simple/tests/00-setup
deleted file mode 100755 (executable)
index f0616a5..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-sudo add-apt-repository ppa:juju/stable -y
-sudo apt-get update
-sudo apt-get install amulet python-requests -y
diff --git a/tests/charms/layers/simple/tests/10-deploy b/tests/charms/layers/simple/tests/10-deploy
deleted file mode 100755 (executable)
index 9a26117..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/python3
-
-import amulet
-import requests
-import unittest
-
-
-class TestCharm(unittest.TestCase):
-    def setUp(self):
-        self.d = amulet.Deployment()
-
-        self.d.add('simple')
-        self.d.expose('simple')
-
-        self.d.setup(timeout=900)
-        self.d.sentry.wait()
-
-        self.unit = self.d.sentry['simple'][0]
-
-    def test_service(self):
-        # test we can access over http
-        page = requests.get('http://{}'.format(self.unit.info['public-address']))
-        self.assertEqual(page.status_code, 200)
-        # Now you can use self.d.sentry[SERVICE][UNIT] to address each of the units and perform
-        # more in-depth steps. Each self.d.sentry[SERVICE][UNIT] has the following methods:
-        # - .info - An array of the information of that unit from Juju
-        # - .file(PATH) - Get the details of a file on that unit
-        # - .file_contents(PATH) - Get plain text output of PATH file from that unit
-        # - .directory(PATH) - Get details of directory
-        # - .directory_contents(PATH) - List files and folders in PATH on that unit
-        # - .relation(relation, service:rel) - Get relation data from return service
-
-        
-if __name__ == '__main__':
-    unittest.main()
index 85a282e..7b2bda9 100644 (file)
@@ -137,5 +137,4 @@ class TestCharm(base.TestN2VC):
                 await asyncio.sleep(15)
 
             print("test_charm_native stopped")
-
         return 'ok'
index e7fa920..81501b0 100644 (file)
@@ -109,7 +109,14 @@ class TestCharm(base.TestN2VC):
                 juju:
                     charm: metrics-proxy-ci
                     proxy: true
-    """
+                initial-config-primitive:
+                -   seq: '1'
+                    name: run
+                    parameter:
+                    -   name: command
+                        data-type: STRING
+                        value: hostname
+"""
 
     # @pytest.mark.serial
     @pytest.mark.asyncio
index b93dfed..e15b790 100644 (file)
@@ -13,9 +13,9 @@ class TestCharm(base.TestN2VC):
     NSD_YAML = """
     nsd:nsd-catalog:
         nsd:
-        -   id: charmnative-ns
-            name: charmnative-ns
-            short-name: charmnative-ns
+        -   id: nonstring-ns
+            name: nonstring-ns
+            short-name: nonstring-ns
             description: NS with 1 VNFs charmnative-vnf connected by datanet and mgmtnet VLs
             version: '1.0'
             logo: osm.png
@@ -143,5 +143,5 @@ class TestCharm(base.TestN2VC):
                 print("Waiting for test to finish...")
                 await asyncio.sleep(15)
             logging.debug("test_charm_non_string_parameter stopped")
-
+            # assert False
         return 'ok'
diff --git a/tox.ini b/tox.ini
index b353ce8..c54d27d 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -15,7 +15,7 @@ markers =
 basepython=python3
 usedevelop=True
 # for testing with other python versions
-commands = py.test --ignore modules/ --tb native -ra -v -s -n auto -k 'not integration' -m 'not serial' {posargs}
+commands = py.test --ignore modules/ --ignore tests/charms/ --tb native -ra -v -s -n auto -k 'not integration' -m 'not serial' {posargs}
 passenv =
     HOME
     VCA_HOST
@@ -38,7 +38,7 @@ deps =
 [testenv:py3]
 # default tox env, excludes integration and serial tests
 commands =
-    pytest --ignore modules/ --tb native -ra -v -s -n auto -k 'not integration' -m 'not serial' {posargs}
+    pytest --ignore modules/ --ignore tests/charms/ --tb native -ra -v -s -n auto -k 'not integration' -m 'not serial' {posargs}
 
 [testenv:lint]
 envdir = {toxworkdir}/py3
@@ -49,7 +49,7 @@ deps =
 
 [testenv:integration]
 envdir = {toxworkdir}/py3
-commands = py.test --ignore modules/ --tb native -ra -v -s -n 1 -k 'integration' -m 'serial' {posargs}
+commands = py.test --ignore modules/  --ignore tests/charms/ --tb native -ra -v -s -n 1 -k 'integration' -m 'serial' {posargs}
 
 [testenv:build]
 deps =