From eaa24281325edf38b4d756d86cace6278bd8cc66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Nowak?= <luke@nexedi.com> Date: Fri, 11 Oct 2019 09:05:41 +0200 Subject: [PATCH] caddy-frontend/test: Switch to slapos.testing.testcase --- software/caddy-frontend/test/test.py | 30 ++- software/caddy-frontend/test/utils.py | 327 -------------------------- 2 files changed, 14 insertions(+), 343 deletions(-) delete mode 100644 software/caddy-frontend/test/utils.py diff --git a/software/caddy-frontend/test/test.py b/software/caddy-frontend/test/test.py index 31330fe43..37bfef383 100644 --- a/software/caddy-frontend/test/test.py +++ b/software/caddy-frontend/test/test.py @@ -54,9 +54,6 @@ try: except ImportError: from backports import lzma -from utils import SlapOSInstanceTestCase -from utils import findFreeTCPPort - import datetime from cryptography import x509 @@ -66,6 +63,13 @@ from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.x509.oid import NameOID +from slapos.testing.testcase import makeModuleSetUpAndTestCaseClass +from slapos.testing.utils import findFreeTCPPort +setUpModule, SlapOSInstanceTestCase = makeModuleSetUpAndTestCaseClass( + os.path.abspath( + os.path.join(os.path.dirname(__file__), '..', 'software.cfg'))) + + SLAPOS_TEST_IPV4 = os.environ['SLAPOS_TEST_IPV4'] SLAPOS_TEST_IPV6 = os.environ['SLAPOS_TEST_IPV6'] @@ -467,12 +471,6 @@ class HttpFrontendTestCase(SlapOSInstanceTestCase): # show full diffs, as it is required for proper analysis of problems maxDiff = None - @classmethod - def getSoftwareURLList(cls): - return ( - os.path.abspath( - os.path.join(os.path.dirname(__file__), '..', 'software.cfg')), ) - @classmethod def setUpClass(cls): super(HttpFrontendTestCase, cls).setUpClass() @@ -797,8 +795,8 @@ class SlaveHttpFrontendTestCase(HttpFrontendTestCase): return False for slave_reference, partition_parameter_kw in cls\ .getSlaveParameterDictDict().items(): - parameter_dict = cls.slapos_controler.slap.registerOpenOrder().request( - software_release=cls.software_url_list[0], + parameter_dict = cls.request( + software_release=cls.getSoftwareURL(), partition_reference=slave_reference, partition_parameter_kw=partition_parameter_kw, shared=True @@ -815,11 +813,11 @@ class SlaveHttpFrontendTestCase(HttpFrontendTestCase): @classmethod def setUpSlaves(cls): cls.slave_connection_parameter_dict_dict = {} - request = cls.slapos_controler.slap.registerOpenOrder().request + request = cls.slap.request for slave_reference, partition_parameter_kw in cls\ .getSlaveParameterDictDict().items(): slave_instance = request( - software_release=cls.software_url_list[0], + software_release=cls.getSoftwareURL(), partition_reference=slave_reference, partition_parameter_kw=partition_parameter_kw, shared=True @@ -830,7 +828,7 @@ class SlaveHttpFrontendTestCase(HttpFrontendTestCase): for slave_reference, partition_parameter_kw in cls\ .getSlaveParameterDictDict().items(): slave_instance = request( - software_release=cls.software_url_list[0], + software_release=cls.getSoftwareURL(), partition_reference=slave_reference, partition_parameter_kw=partition_parameter_kw, shared=True @@ -5917,8 +5915,8 @@ class TestSlaveSlapOSMasterCertificateCompatibility( ssl_ca_crt=ca.certificate_pem, ) - self.slapos_controler.slap.registerOpenOrder().request( - software_release=self.software_url_list[0], + self.request( + software_release=self.cls.getSoftwareURL(), partition_reference='custom_domain_ssl_crt_ssl_key_ssl_ca_crt', partition_parameter_kw=slave_parameter_dict, shared=True diff --git a/software/caddy-frontend/test/utils.py b/software/caddy-frontend/test/utils.py deleted file mode 100644 index 85867b0fd..000000000 --- a/software/caddy-frontend/test/utils.py +++ /dev/null @@ -1,327 +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. - @classmethod - def getSupervisorRPCServer(cls): - """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( - **cls.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': 'T', # minimise path length, see https://github.com/apache/trafficserver/issues/2421 - # "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, max_quantity=None): - """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. - """ - run_cp_kw = {} - if max_quantity is not None: - run_cp_kw['max_quantity'] = max_quantity - 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, - **run_cp_kw) - 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