forked from shaba/openuds
Added more tests and a couple of cosmetic fixes
This commit is contained in:
parent
db12077304
commit
8b540c350a
@ -49,7 +49,7 @@ class AssignedAndUnusedTests(UDSTransactionTestCase):
|
||||
config.GlobalConfig.CHECK_UNUSED_TIME.set('600')
|
||||
AssignedAndUnused.setup()
|
||||
# All created user services has "in_use" to False, os_state and state to USABLE
|
||||
self.userServices = fixtures_services.newUserServiceForTesting(count=32)
|
||||
self.userServices = fixtures_services.createUserServiceForTesting(count=32)
|
||||
|
||||
def test_assigned_unused(self):
|
||||
for us in self.userServices: # Update state date to now
|
||||
|
@ -53,7 +53,7 @@ class HangedCleanerTests(UDSTransactionTestCase):
|
||||
config.GlobalConfig.MAX_REMOVAL_TIME.set(MAX_INIT)
|
||||
HangedCleaner.setup()
|
||||
# All created user services has "in_use" to False, os_state and state to USABLE
|
||||
self.userServices = fixtures_services.newUserServiceForTesting(
|
||||
self.userServices = fixtures_services.createUserServiceForTesting(
|
||||
count=TEST_SERVICES
|
||||
)
|
||||
|
||||
|
134
server/src/tests/core/workers/test_servicepools_cache_updater.py
Normal file
134
server/src/tests/core/workers/test_servicepools_cache_updater.py
Normal file
@ -0,0 +1,134 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2022 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import typing
|
||||
import logging
|
||||
|
||||
|
||||
from uds.core.util.state import State
|
||||
from uds.core.workers.servicepools_cache_updater import ServiceCacheUpdater
|
||||
from uds.core.environment import Environment
|
||||
|
||||
from ...utils.test import UDSTransactionTestCase
|
||||
from ...fixtures import services as services_fixtures
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from uds import models
|
||||
|
||||
class ServiceCacheUpdaterTests(UDSTransactionTestCase):
|
||||
servicePool: 'models.ServicePool'
|
||||
|
||||
def setUp(self) -> None:
|
||||
ServiceCacheUpdater.setup()
|
||||
userService = services_fixtures.createUserServiceForTesting()[0]
|
||||
self.servicePool = userService.deployed_service
|
||||
userService.delete() # empty all
|
||||
|
||||
def numberOfRemovingOrCanced(self) -> int:
|
||||
return self.servicePool.userServices.filter(state__in=[State.REMOVABLE, State.CANCELED]).count()
|
||||
|
||||
def runCacheUpdater(self, times: int) -> int:
|
||||
for _ in range(times):
|
||||
updater = ServiceCacheUpdater(Environment.getTempEnv())
|
||||
updater.run()
|
||||
# Test user service will cancel automatically so it will not get in "removable" state (on remove start, it will tell it has been removed)
|
||||
return self.servicePool.userServices.count() - self.numberOfRemovingOrCanced()
|
||||
|
||||
def setCache(self, initial: typing.Optional[int] = None, cache: typing.Optional[int] = None, cache2: typing.Optional[int] = None, max: typing.Optional[int] = None) -> None:
|
||||
self.servicePool.initial_srvs = self.servicePool.initial_srvs if initial is None else initial
|
||||
self.servicePool.cache_l1_srvs = self.servicePool.cache_l1_srvs if cache is None else cache
|
||||
self.servicePool.cache_l2_srvs = self.servicePool.cache_l2_srvs if cache2 is None else cache2
|
||||
self.servicePool.max_srvs = self.servicePool.max_srvs if max is None else max
|
||||
self.servicePool.save()
|
||||
|
||||
def test_initial(self) -> None:
|
||||
self.setCache(initial=100, cache=10, max=500)
|
||||
|
||||
self.assertEqual(self.runCacheUpdater(self.servicePool.initial_srvs + 10), self.servicePool.initial_srvs)
|
||||
|
||||
def test_remove(self) -> None:
|
||||
self.setCache(initial=100, cache=110, max=500)
|
||||
|
||||
self.runCacheUpdater(self.servicePool.cache_l1_srvs)
|
||||
|
||||
# Now again, decrease cache to original, must remove ten elements
|
||||
mustDelete = self.servicePool.cache_l1_srvs - self.servicePool.initial_srvs
|
||||
|
||||
self.setCache(cache=10)
|
||||
self.assertEqual(self.runCacheUpdater(mustDelete), self.servicePool.initial_srvs)
|
||||
|
||||
self.assertEqual(self.numberOfRemovingOrCanced(), mustDelete)
|
||||
|
||||
def test_max(self) -> None:
|
||||
self.setCache(initial=100, cache=10, max=50)
|
||||
self.assertEqual(self.runCacheUpdater(self.servicePool.initial_srvs + 10), self.servicePool.max_srvs)
|
||||
|
||||
self.setCache(cache=200)
|
||||
self.assertEqual(self.runCacheUpdater(self.servicePool.initial_srvs + 10), self.servicePool.max_srvs)
|
||||
|
||||
def test_cache(self) -> None:
|
||||
self.setCache(initial=10, cache=100, max=500)
|
||||
|
||||
# Try to "overcreate" cache elements (must create 100, that is "cache" (bigger than initial))
|
||||
self.assertEqual(self.runCacheUpdater(self.servicePool.cache_l1_srvs + 10), self.servicePool.cache_l1_srvs)
|
||||
|
||||
def test_provider_preparing_limits(self) -> None:
|
||||
from uds.services.Test.provider import TestProvider
|
||||
|
||||
TestProvider.maxPreparingServices = 10
|
||||
self.setCache(initial=100, cache=10, max=50)
|
||||
|
||||
# Try to "overcreate" cache elements but provider limits it to 10
|
||||
self.assertEqual(self.runCacheUpdater(self.servicePool.cache_l1_srvs + 10), 10)
|
||||
|
||||
# Delete all userServices
|
||||
self.servicePool.userServices.all().delete()
|
||||
|
||||
# Now, set provider limit to 0. Minumum aceptable is 1, so 1 will be created
|
||||
TestProvider.maxPreparingServices = 0
|
||||
self.assertEqual(self.runCacheUpdater(self.servicePool.cache_l1_srvs + 10), 1)
|
||||
|
||||
def test_provider_removing_limits(self) -> None:
|
||||
from uds.services.Test.provider import TestProvider
|
||||
|
||||
TestProvider.maxRemovingServices = 10
|
||||
self.setCache(initial=0, cache=50, max=50)
|
||||
|
||||
# Try to "overcreate" cache elements but provider limits it to 10
|
||||
self.runCacheUpdater(self.servicePool.cache_l1_srvs)
|
||||
|
||||
# Now set cache to a lower value
|
||||
self.setCache(cache=10)
|
||||
|
||||
# Execute updater, must remove 10 elements (maxRemovingServices)
|
||||
self.assertEqual(self.runCacheUpdater(10), 40)
|
||||
|
||||
|
2
server/src/tests/fixtures/services.py
vendored
2
server/src/tests/fixtures/services.py
vendored
@ -155,7 +155,7 @@ def createSingleTestingUserServiceStructure(
|
||||
return user_service
|
||||
|
||||
|
||||
def newUserServiceForTesting(
|
||||
def createUserServiceForTesting(
|
||||
count: int = 1,
|
||||
type_: typing.Union[
|
||||
typing.Literal['managed'], typing.Literal['unmanaged']
|
||||
|
@ -94,7 +94,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
|
||||
states = [State.PREPARING, State.USABLE, State.REMOVING, State.REMOVABLE]
|
||||
return Q(state__in=states)
|
||||
|
||||
def __checkMaxDeployedReached(self, servicePool: ServicePool) -> None:
|
||||
def _checkMaxDeployedReached(self, servicePool: ServicePool) -> None:
|
||||
"""
|
||||
Checks if maxDeployed for the service has been reached, and, if so,
|
||||
raises an exception that no more services of this kind can be reached
|
||||
@ -105,7 +105,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
|
||||
return
|
||||
|
||||
numberOfServices = servicePool.userServices.filter(
|
||||
state__in=[State.PREPARING, State.USABLE]
|
||||
self.getStateFilter(servicePool)
|
||||
).count()
|
||||
|
||||
if serviceInstance.maxDeployed <= numberOfServices:
|
||||
@ -113,14 +113,14 @@ class UserServiceManager(metaclass=singleton.Singleton):
|
||||
'Max number of allowed deployments for service reached'
|
||||
)
|
||||
|
||||
def __createCacheAtDb(
|
||||
def _createCacheAtDb(
|
||||
self, publication: ServicePoolPublication, cacheLevel: int
|
||||
) -> UserService:
|
||||
"""
|
||||
Private method to instatiate a cache element at database with default states
|
||||
"""
|
||||
# Checks if maxDeployed has been reached and if so, raises an exception
|
||||
self.__checkMaxDeployedReached(publication.deployed_service)
|
||||
self._checkMaxDeployedReached(publication.deployed_service)
|
||||
now = getSqlDatetime()
|
||||
return publication.userServices.create(
|
||||
cache_level=cacheLevel,
|
||||
@ -134,13 +134,13 @@ class UserServiceManager(metaclass=singleton.Singleton):
|
||||
in_use=False,
|
||||
)
|
||||
|
||||
def __createAssignedAtDb(
|
||||
def _createAssignedAtDb(
|
||||
self, publication: ServicePoolPublication, user: User
|
||||
) -> UserService:
|
||||
"""
|
||||
Private method to instatiate an assigned element at database with default state
|
||||
"""
|
||||
self.__checkMaxDeployedReached(publication.deployed_service)
|
||||
self._checkMaxDeployedReached(publication.deployed_service)
|
||||
now = getSqlDatetime()
|
||||
return publication.userServices.create(
|
||||
cache_level=0,
|
||||
@ -154,7 +154,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
|
||||
in_use=False,
|
||||
)
|
||||
|
||||
def __createAssignedAtDbForNoPublication(
|
||||
def _createAssignedAtDbForNoPublication(
|
||||
self, servicePool: ServicePool, user: User
|
||||
) -> UserService:
|
||||
"""
|
||||
@ -162,7 +162,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
|
||||
There is cases where deployed services do not have publications (do not need them), so we need this method to create
|
||||
an UserService with no publications, and create them from an ServicePool
|
||||
"""
|
||||
self.__checkMaxDeployedReached(servicePool)
|
||||
self._checkMaxDeployedReached(servicePool)
|
||||
now = getSqlDatetime()
|
||||
return servicePool.userServices.create(
|
||||
cache_level=0,
|
||||
@ -187,7 +187,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
|
||||
cacheLevel,
|
||||
publication,
|
||||
)
|
||||
cache = self.__createCacheAtDb(publication, cacheLevel)
|
||||
cache = self._createCacheAtDb(publication, cacheLevel)
|
||||
ci = cache.getInstance()
|
||||
state = ci.deployForCache(cacheLevel)
|
||||
|
||||
@ -214,7 +214,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
|
||||
publication,
|
||||
)
|
||||
if publication:
|
||||
assigned = self.__createAssignedAtDb(publication, user)
|
||||
assigned = self._createAssignedAtDb(publication, user)
|
||||
else:
|
||||
raise Exception(
|
||||
'Invalid publication creating service assignation: {} {}'.format(
|
||||
@ -223,7 +223,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
|
||||
)
|
||||
else:
|
||||
logger.debug('Creating a new assigned element for user %s', user)
|
||||
assigned = self.__createAssignedAtDbForNoPublication(servicePool, user)
|
||||
assigned = self._createAssignedAtDbForNoPublication(servicePool, user)
|
||||
|
||||
assignedInstance = assigned.getInstance()
|
||||
state = assignedInstance.deployForUser(user)
|
||||
@ -251,7 +251,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
|
||||
publication,
|
||||
)
|
||||
if publication:
|
||||
assigned = self.__createAssignedAtDb(publication, user)
|
||||
assigned = self._createAssignedAtDb(publication, user)
|
||||
else:
|
||||
raise Exception(
|
||||
'Invalid publication creating service assignation: {} {}'.format(
|
||||
@ -264,7 +264,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
|
||||
assignableId,
|
||||
user,
|
||||
)
|
||||
assigned = self.__createAssignedAtDbForNoPublication(servicePool, user)
|
||||
assigned = self._createAssignedAtDbForNoPublication(servicePool, user)
|
||||
|
||||
# Now, get from serviceInstance the data
|
||||
assignedInstance = assigned.getInstance()
|
||||
|
@ -390,7 +390,7 @@ class Service(Module):
|
||||
@classmethod
|
||||
def canAssign(cls) -> bool:
|
||||
"""
|
||||
Helper to query if a class is custom (implements getJavascript method)
|
||||
Helper to query if a class is assignable (can be assigned to an user manually)
|
||||
"""
|
||||
return (
|
||||
cls.listAssignables is not Service.listAssignables
|
||||
|
@ -71,15 +71,11 @@ class TestProvider(services.ServiceProvider):
|
||||
# : mark it as _ (using gettext_noop)
|
||||
iconFile = 'provider.png'
|
||||
|
||||
# now comes the form fields
|
||||
# There is always two fields that are requested to the admin, that are:
|
||||
# Service Name, that is a name that the admin uses to name this provider
|
||||
# Description, that is a short description that the admin gives to this provider
|
||||
# Now we are going to add a few fields that we need to use this provider
|
||||
# Remember that these are "dummy" fields, that in fact are not required
|
||||
# but used for sample purposes
|
||||
# If we don't indicate an order, the output order of fields will be
|
||||
# "random"
|
||||
# Max preparing concurrent services
|
||||
maxPreparingServices = 1000 # a lot, this in fact will not make anything
|
||||
|
||||
# Mas removing concurrent services
|
||||
maxRemovingServices = 1000 # a lot, this in fact will not make anything
|
||||
|
||||
# Simple data for testing pourposes
|
||||
@dataclasses.dataclass
|
||||
|
Loading…
x
Reference in New Issue
Block a user