From 7830efb57c4b3d8ea431e099c0ab59d6874c2ab1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Tue, 26 Mar 2019 01:28:56 +0100
Subject: [PATCH] re6stnet/test: use slapos.testing

---
 software/re6stnet/test/setup.py |  34 ++--
 software/re6stnet/test/test.py  |  66 +++----
 software/re6stnet/test/utils.py | 322 --------------------------------
 3 files changed, 41 insertions(+), 381 deletions(-)
 delete mode 100644 software/re6stnet/test/utils.py

diff --git a/software/re6stnet/test/setup.py b/software/re6stnet/test/setup.py
index adfc9dfbe..0ffe8690b 100644
--- a/software/re6stnet/test/setup.py
+++ b/software/re6stnet/test/setup.py
@@ -25,30 +25,30 @@
 #
 ##############################################################################
 from setuptools import setup, find_packages
-import glob
-import os
 
 version = '0.0.1.dev0'
 name = 'slapos.test.re6stnet'
-long_description = open("README.md").read()
+with open("README.md") as f:
+  long_description = f.read()
 
-setup(name=name,
-      version=version,
-      description="Test for SlapOS' Re6stnet",
-      long_description=long_description,
-      long_description_content_type='text/markdown',
-      maintainer="Nexedi",
-      maintainer_email="info@nexedi.com",
-      url="https://lab.nexedi.com/nexedi/slapos",
-      packages=find_packages(),
-      install_requires=[
+setup(
+    name=name,
+    version=version,
+    description="Test for SlapOS' Re6stnet",
+    long_description=long_description,
+    long_description_content_type='text/markdown',
+    maintainer="Nexedi",
+    maintainer_email="info@nexedi.com",
+    url="https://lab.nexedi.com/nexedi/slapos",
+    packages=find_packages(),
+    install_requires=[
         'slapos.core',
         'slapos.cookbook',
         'slapos.libnetworkcache',
         'erp5.util',
         'supervisor',
         'psutil',
-        ],
-      zip_safe=True,
-      test_suite='test',
-    )
+    ],
+    zip_safe=True,
+    test_suite='test',
+)
diff --git a/software/re6stnet/test/test.py b/software/re6stnet/test/test.py
index b4c5ece9d..54fadd1bb 100644
--- a/software/re6stnet/test/test.py
+++ b/software/re6stnet/test/test.py
@@ -26,60 +26,39 @@
 ##############################################################################
 
 import os
-import shutil
-import urlparse
-import tempfile
 import requests
-import socket
-import StringIO
-import subprocess
 import json
 
-import utils
 from slapos.recipe.librecipe import generateHashFromFiles
+from slapos.testing.testcase import makeModuleSetUpAndTestCaseClass
 
-SLAPOS_TEST_IPV4 = os.environ['SLAPOS_TEST_IPV4']
-SLAPOS_TEST_IPV6 = os.environ['SLAPOS_TEST_IPV6']
+setUpModule, Re6stnetTestCase = makeModuleSetUpAndTestCaseClass(
+    os.path.abspath(
+        os.path.join(os.path.dirname(__file__), '..', 'software.cfg')))
 
 
-# for development: debugging logs and install Ctrl+C handler
-if os.environ.get('SLAPOS_TEST_DEBUG'):
-  import logging
-  logging.basicConfig(level=logging.DEBUG)
-  import unittest
-  unittest.installHandler()
-
-
-class Re6stnetTestCase(utils.SlapOSInstanceTestCase):
-  def setUp(self):
-    import logging
-
-    utils.SlapOSInstanceTestCase.setUp(self)
-    self.logger = logging.getLogger(__name__)
-
-  @classmethod
-  def getSoftwareURLList(cls):
-    return (os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'software.cfg')), )
-
 class TestRe6stnetRegistry(Re6stnetTestCase):
-
   def test_listen(self):
     connection_parameters = self.computer_partition.getConnectionParameterDict()
     registry_url = connection_parameters['re6stry-local-url']
 
     _ = requests.get(registry_url)
 
-class TestPortRedirection(Re6stnetTestCase):
 
+class TestPortRedirection(Re6stnetTestCase):
   def test_portredir_config(self):
-    portredir_config_path = os.path.join(self.computer_partition_root_path, '.slapos-port-redirect')
+    portredir_config_path = os.path.join(
+        self.computer_partition_root_path, '.slapos-port-redirect')
     with open(portredir_config_path) as f:
       portredir_config = json.load(f)
 
-    self.assertDictContainsSubset({
-      'srcPort': 9201,
-      'destPort': 9201,
-    }, portredir_config[0])
+    self.assertDictContainsSubset(
+        {
+            'srcPort': 9201,
+            'destPort': 9201,
+        }, portredir_config[0])
+
+
 
 class ServicesTestCase(Re6stnetTestCase):
 
@@ -89,18 +68,21 @@ class ServicesTestCase(Re6stnetTestCase):
 
   def test_hashes(self):
     hash_files = [
-      'software_release/buildout.cfg',
+        'software_release/buildout.cfg',
     ]
     expected_process_names = [
-      'httpd-{hash}-on-watch',
+        'httpd-{hash}-on-watch',
     ]
 
-    supervisor = self.getSupervisorRPCServer().supervisor
-    process_names = [process['name']
-                     for process in supervisor.getAllProcessInfo()]
+    with self.slap.instance_supervisor_rpc as supervisor:
+      process_names = [
+          process['name'] for process in supervisor.getAllProcessInfo()
+      ]
 
-    hash_files = [os.path.join(self.computer_partition_root_path, path)
-                  for path in hash_files]
+    hash_files = [
+        os.path.join(self.computer_partition_root_path, path)
+        for path in hash_files
+    ]
 
     for name in expected_process_names:
       h = generateHashFromFiles(hash_files)
diff --git a/software/re6stnet/test/utils.py b/software/re6stnet/test/utils.py
deleted file mode 100644
index f5fb6c0de..000000000
--- a/software/re6stnet/test/utils.py
+++ /dev/null
@@ -1,322 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2018 Nexedi SA and Contributors. All Rights Reserved.
-#
-# WARNING: This program as such is intended to be used by professional
-# programmers who take the whole responsibility of assessing all potential
-# consequences resulting from its eventual inadequacies and bugs
-# End users who are looking for a ready-to-use solution with commercial
-# guarantees and support are strongly adviced to contract a Free Software
-# Service Company
-#
-# This program is Free Software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 3
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-#
-##############################################################################
-
-import unittest
-import os
-import socket
-from contextlib import closing
-import logging
-import StringIO
-import xmlrpclib
-
-import supervisor.xmlrpc
-from erp5.util.testnode.SlapOSControler import SlapOSControler
-from erp5.util.testnode.ProcessManager import ProcessManager
-
-
-# Utility functions
-def findFreeTCPPort(ip=''):
-  """Find a free TCP port to listen to.
-  """
-  family = socket.AF_INET6 if ':' in ip else socket.AF_INET
-  with closing(socket.socket(family, socket.SOCK_STREAM)) as s:
-    s.bind((ip, 0))
-    return s.getsockname()[1]
-
-
-# TODO:
-#  - allow requesting multiple instances ?
-
-class SlapOSInstanceTestCase(unittest.TestCase):
-  """Install one slapos instance.
-
-  This test case install software(s) and request one instance during `setUpClass`
-  and destroy the instance during `tearDownClass`.
-
-  Software Release URL, Instance Software Type and Instance Parameters can be defined
-  on the class.
-
-  All tests from the test class will run with the same instance.
-
-  The following class attributes are available:
-
-    * `computer_partition`:  the computer partition instance, implementing
-    `slapos.slap.interface.slap.IComputerPartition`.
-
-    * `computer_partition_root_path`: the path of the instance root directory.
-
-  """
-
-  # Methods to be defined by subclasses.
-  @classmethod
-  def getSoftwareURLList(cls):
-    """Return URL of software releases to install.
-
-    To be defined by subclasses.
-    """
-    raise NotImplementedError()
-
-  @classmethod
-  def getInstanceParameterDict(cls):
-    """Return instance parameters
-
-    To be defined by subclasses if they need to request instance with specific
-    parameters.
-    """
-    return {}
-
-  @classmethod
-  def getInstanceSoftwareType(cls):
-    """Return software type for instance, default "default"
-
-    To be defined by subclasses if they need to request instance with specific
-    software type.
-    """
-    return "default"
-
-  # Utility methods.
-  def getSupervisorRPCServer(self):
-    """Returns a XML-RPC connection to the supervisor used by slapos node
-
-    Refer to http://supervisord.org/api.html for details of available methods.
-    """
-    # xmlrpc over unix socket https://stackoverflow.com/a/11746051/7294664
-    return xmlrpclib.ServerProxy(
-       'http://slapos-supervisor',
-       transport=supervisor.xmlrpc.SupervisorTransport(
-           None,
-           None,
-           # XXX hardcoded socket path
-           serverurl="unix://{working_directory}/inst/supervisord.socket".format(
-             **self.config)))
-
-  # Unittest methods
-  @classmethod
-  def setUpClass(cls):
-    """Setup the class, build software and request an instance.
-
-    If you have to override this method, do not forget to call this method on
-    parent class.
-    """
-    try:
-      cls.setUpWorkingDirectory()
-      cls.setUpConfig()
-      cls.setUpSlapOSController()
-
-      cls.runSoftwareRelease()
-      # XXX instead of "runSoftwareRelease", it would be better to be closer to slapos usage:
-      # cls.supplySoftwares()
-      # cls.installSoftwares()
-
-      cls.runComputerPartition()
-      # XXX instead of "runComputerPartition", it would be better to be closer to slapos usage:
-      # cls.requestInstances()
-      # cls.createInstances()
-      # cls.requestInstances()
-
-    except Exception:
-      cls.stopSlapOSProcesses()
-      raise
-
-  @classmethod
-  def tearDownClass(cls):
-    """Tear down class, stop the processes and destroy instance.
-    """
-    cls.stopSlapOSProcesses()
-
-  # Implementation
-  @classmethod
-  def stopSlapOSProcesses(cls):
-    if hasattr(cls, '_process_manager'):
-      cls._process_manager.killPreviousRun()
-
-  @classmethod
-  def setUpWorkingDirectory(cls):
-    """Initialise the directories"""
-    cls.working_directory = os.environ.get(
-        'SLAPOS_TEST_WORKING_DIR',
-        os.path.join(os.path.dirname(__file__), '.slapos'))
-    # To prevent error: Cannot open an HTTP server: socket.error reported
-    # AF_UNIX path too long This `working_directory` should not be too deep.
-    # Socket path is 108 char max on linux
-    # https://github.com/torvalds/linux/blob/3848ec5/net/unix/af_unix.c#L234-L238
-    # Supervisord socket name contains the pid number, which is why we add
-    # .xxxxxxx in this check.
-    if len(cls.working_directory + '/inst/supervisord.socket.xxxxxxx') > 108:
-      raise RuntimeError('working directory ( {} ) is too deep, try setting '
-              'SLAPOS_TEST_WORKING_DIR'.format(cls.working_directory))
-
-    if not os.path.exists(cls.working_directory):
-      os.mkdir(cls.working_directory)
-
-  @classmethod
-  def setUpConfig(cls):
-    """Create slapos configuration"""
-    cls.config = {
-      "working_directory": cls.working_directory,
-      "slapos_directory": cls.working_directory,
-      "log_directory": cls.working_directory,
-      "computer_id": 'slapos.test',  # XXX
-      'proxy_database': os.path.join(cls.working_directory, 'proxy.db'),
-      'partition_reference': cls.__name__,
-      # "proper" slapos command must be in $PATH
-      'slapos_binary': 'slapos',
-      'node_quantity': '3',
-    }
-    # Some tests are expecting that local IP is not set to 127.0.0.1
-    ipv4_address = os.environ.get('SLAPOS_TEST_IPV4', '127.0.1.1')
-    ipv6_address = os.environ['SLAPOS_TEST_IPV6']
-
-    cls.config['proxy_host'] = cls.config['ipv4_address'] = ipv4_address
-    cls.config['ipv6_address'] = ipv6_address
-    cls.config['proxy_port'] = findFreeTCPPort(ipv4_address)
-    cls.config['master_url'] = 'http://{proxy_host}:{proxy_port}'.format(
-      **cls.config)
-
-  @classmethod
-  def setUpSlapOSController(cls):
-    """Create the a "slapos controller" and supply softwares from `getSoftwareURLList`.
-
-    This is equivalent to:
-
-    slapos proxy start
-    for sr in getSoftwareURLList; do
-      slapos supply $SR $COMP
-    done
-    """
-    cls._process_manager = ProcessManager()
-
-    # XXX this code is copied from testnode code
-    cls.slapos_controler = SlapOSControler(
-        cls.working_directory,
-        cls.config
-    )
-
-    slapproxy_log = os.path.join(cls.config['log_directory'], 'slapproxy.log')
-    logger = logging.getLogger(__name__)
-    logger.debug('Configured slapproxy log to %r', slapproxy_log)
-
-    cls.software_url_list = cls.getSoftwareURLList()
-    cls.slapos_controler.initializeSlapOSControler(
-        slapproxy_log=slapproxy_log,
-        process_manager=cls._process_manager,
-        reset_software=False,
-        software_path_list=cls.software_url_list)
-
-    # XXX we should check *earlier* if that pidfile exist and if supervisord
-    # process still running, because if developer started supervisord (or bugs?)
-    # then another supervisord will start and starting services a second time
-    # will fail.
-    cls._process_manager.supervisord_pid_file = os.path.join(
-      cls.slapos_controler.instance_root, 'var', 'run', 'supervisord.pid')
-
-  @classmethod
-  def runSoftwareRelease(cls):
-    """Run all the software releases that were supplied before.
-
-    This is the equivalent of `slapos node software`.
-
-    The tests will be marked file if software building fail.
-    """
-    logger = logging.getLogger()
-    logger.level = logging.DEBUG
-    stream = StringIO.StringIO()
-    stream_handler = logging.StreamHandler(stream)
-    logger.addHandler(stream_handler)
-
-    try:
-      cls.software_status_dict = cls.slapos_controler.runSoftwareRelease(
-        cls.config, environment=os.environ)
-      stream.seek(0)
-      stream.flush()
-      message = ''.join(stream.readlines()[-100:])
-      assert cls.software_status_dict['status_code'] == 0, message
-    finally:
-      logger.removeHandler(stream_handler)
-      del stream
-
-
-  @classmethod
-  def runComputerPartition(cls):
-    """Instanciate the software.
-
-    This is the equivalent of doing:
-
-    slapos request --type=getInstanceSoftwareType --parameters=getInstanceParameterDict
-    slapos node instance
-
-    and return the slapos request instance parameters.
-
-    This can be called by tests to simulate re-request with different parameters.
-    """
-    logger = logging.getLogger()
-    logger.level = logging.DEBUG
-    stream = StringIO.StringIO()
-    stream_handler = logging.StreamHandler(stream)
-    logger.addHandler(stream_handler)
-
-    if cls.getInstanceSoftwareType() != 'default':
-      raise NotImplementedError
-
-    instance_parameter_dict = cls.getInstanceParameterDict()
-    try:
-      cls.instance_status_dict = cls.slapos_controler.runComputerPartition(
-        cls.config,
-        cluster_configuration=instance_parameter_dict,
-        environment=os.environ)
-      stream.seek(0)
-      stream.flush()
-      message = ''.join(stream.readlines()[-100:])
-      assert cls.instance_status_dict['status_code'] == 0, message
-    finally:
-      logger.removeHandler(stream_handler)
-      del stream
-
-    # FIXME: similar to test node, only one (root) partition is really
-    #        supported for now.
-    computer_partition_list = []
-    for i in range(len(cls.software_url_list)):
-      computer_partition_list.append(
-          cls.slapos_controler.slap.registerOpenOrder().request(
-            cls.software_url_list[i],
-            # This is how testnode's SlapOSControler name created partitions
-            partition_reference='testing partition {i}'.format(
-              i=i, **cls.config),
-            partition_parameter_kw=instance_parameter_dict))
-
-    # expose some class attributes so that tests can use them:
-    # the ComputerPartition instances, to getInstanceParameterDict
-    cls.computer_partition = computer_partition_list[0]
-
-    # the path of the instance on the filesystem, for low level inspection
-    cls.computer_partition_root_path = os.path.join(
-        cls.config['working_directory'],
-        'inst',
-        cls.computer_partition.getId())
-
-
-
-- 
2.30.9