From 072c8ef9b48e18dfe47e61c9fad62521399b54c0 Mon Sep 17 00:00:00 2001
From: Agate Berriot <aberriot@entrouvert.com>
Date: Thu, 28 Jul 2022 10:10:49 +0200
Subject: [PATCH 2/6] django32: do not instanciate ServiceBase abstract model
 (#67760)

---
 hobo/environment/models.py                | 36 +++++++++++++----------
 hobo/environment/validators.py            |  7 ++---
 tests/test_cook.py                        | 17 ++++++-----
 tests/test_environment.py                 |  5 ++--
 tests_schemas/test_cook.py                | 12 ++++----
 tests_schemas/test_rename_hobo_service.py | 10 +++----
 tests_schemas/test_rename_service.py      | 16 +++++-----
 7 files changed, 55 insertions(+), 48 deletions(-)

diff --git a/hobo/environment/models.py b/hobo/environment/models.py
index 31bc5de..456c211 100644
--- a/hobo/environment/models.py
+++ b/hobo/environment/models.py
@@ -97,6 +97,25 @@ class Variable(models.Model):
         self._parse_value_as_json()
 
 
+def is_resolvable(url):
+    try:
+        netloc = urllib.parse.urlparse(url).netloc
+        if netloc and socket.gethostbyname(netloc):
+            return True
+    except socket.gaierror:
+        return False
+
+
+def has_valid_certificate(url):
+    try:
+        requests.get(url, timeout=5, verify=True, allow_redirects=False)
+        return True
+    except requests.exceptions.SSLError:
+        return False
+    except requests.exceptions.ConnectionError:
+        return False
+
+
 class ServiceBase(models.Model):
     class Meta:
         abstract = True
@@ -228,23 +247,10 @@ class ServiceBase(models.Model):
         return self.get_base_url_path() + '__provision__/'
 
     def is_resolvable(self):
-        try:
-            netloc = urllib.parse.urlparse(self.base_url).netloc
-            if netloc and socket.gethostbyname(netloc):
-                return True
-        except socket.gaierror:
-            return False
+        return is_resolvable(self.base_url)
 
     def has_valid_certificate(self):
-        if not self.is_resolvable():
-            return False
-        try:
-            requests.get(self.base_url, timeout=5, verify=True, allow_redirects=False)
-            return True
-        except requests.exceptions.SSLError:
-            return False
-        except requests.exceptions.ConnectionError:
-            return False
+        return has_valid_certificate(self.base_url)
 
     def is_running(self):
         if not self.is_resolvable():
diff --git a/hobo/environment/validators.py b/hobo/environment/validators.py
index 047e186..fa20c8c 100644
--- a/hobo/environment/validators.py
+++ b/hobo/environment/validators.py
@@ -3,18 +3,17 @@ import urllib.parse
 from django.core.exceptions import ValidationError
 from django.utils.translation import gettext_lazy as _
 
-from hobo.environment.models import ServiceBase
+from hobo.environment import models
 
 
 def validate_service_url(url):
-    service = ServiceBase(title='dummy', base_url=url)
-    if not service.is_resolvable():
+    if not models.is_resolvable(url):
         raise ValidationError(
             _('Error: %(netloc)s is not resolvable in URL %(url)s'),
             code='not-resolvable',
             params={'netloc': urllib.parse.urlsplit(url).netloc, 'url': url},
         )
-    if not service.has_valid_certificate():
+    if not models.has_valid_certificate(url):
         raise ValidationError(
             _('Error: no valid certificate for %(url)s'), code='invalid-certificate', params={'url': url}
         )
diff --git a/tests/test_cook.py b/tests/test_cook.py
index cfbad8f..f246b23 100644
--- a/tests/test_cook.py
+++ b/tests/test_cook.py
@@ -9,6 +9,7 @@ from django.contrib.contenttypes.models import ContentType
 from django.core.management import call_command
 from django.core.management.base import CommandError
 
+from hobo.environment import models as environment_models
 from hobo.environment.management.commands.cook import Command
 from hobo.environment.models import (
     Authentic,
@@ -57,8 +58,8 @@ def test_check_action(command, monkeypatch):
     command.server_action = 'mock a server_action handler (ex: hobo-create)'
     action, action_args = 'server-action', {'url': 'https://test.org/'}
 
-    monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: True)
-    monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: True)
+    monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: True)
+    monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: True)
     command.check_action(action, action_args)
     assert True
 
@@ -68,8 +69,8 @@ def test_check_action_unknown_action(command, monkeypatch):
     command.server_action = 'mock a server_action handler (ex: hobo-create)'
     action, action_args = 'not-a-server-action', {'url': 'https://test.org/'}
 
-    monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: True)
-    monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: True)
+    monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: True)
+    monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: True)
     with pytest.raises(CommandError) as e_info:
         command.check_action(action, action_args)
     assert 'Unknown action not-a-server-action' in str(e_info.value)
@@ -80,8 +81,8 @@ def test_check_action_unresolvable(command, monkeypatch):
     command.server_action = 'mock a server_action handler (ex: hobo-create)'
     action, action_args = 'server-action', {'url': 'https://test.org/'}
 
-    monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: False)
-    monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: True)
+    monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: False)
+    monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: True)
     with pytest.raises(CommandError) as e_info:
         command.check_action(action, action_args)
     assert 'test.org is not resolvable in URL https://test.org/' in str(e_info.value)
@@ -92,8 +93,8 @@ def test_check_action_invalid_certificat(command, monkeypatch):
     command.server_action = 'mock a server_action handler (ex: hobo-create)'
     action, action_args = 'server-action', {'url': 'https://test.org/'}
 
-    monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: True)
-    monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: False)
+    monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: True)
+    monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: False)
     with pytest.raises(CommandError) as e_info:
         command.check_action(action, action_args)
     assert 'no valid certificate for https://test.org/' in str(e_info.value)
diff --git a/tests/test_environment.py b/tests/test_environment.py
index 07390c9..8c5949f 100644
--- a/tests/test_environment.py
+++ b/tests/test_environment.py
@@ -8,6 +8,7 @@ from django.utils import timezone
 from test_manager import login
 from webtest import Upload
 
+from hobo.environment import models as environment_models
 from hobo.environment.models import AVAILABLE_SERVICES, Combo, Passerelle, ServiceBase, Variable
 from hobo.environment.utils import get_installed_services_dict
 from hobo.profile.models import AttributeDefinition
@@ -143,13 +144,13 @@ def test_service_creation_url_validation(app, admin_user, monkeypatch):
     response = form.submit()
     assert 'not resolvable' in response
 
-    monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: True)
+    monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: True)
     form = response.form
     response = form.submit()
     assert 'no valid certificate' in response
 
     assert not Combo.objects.exists()
-    monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: True)
+    monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: True)
     form = response.form
     response = form.submit()
     assert Combo.objects.exists()
diff --git a/tests_schemas/test_cook.py b/tests_schemas/test_cook.py
index 500e42a..7235377 100644
--- a/tests_schemas/test_cook.py
+++ b/tests_schemas/test_cook.py
@@ -5,18 +5,18 @@ from django.core.management import call_command, load_command_class
 from django.core.management.base import CommandError
 
 from hobo.deploy.utils import get_hobo_json
-from hobo.environment.models import ServiceBase
+from hobo.environment import models as environment_models
 
 
 def test_cook(db, fake_notify, monkeypatch):
-    monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: True)
-    monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: True)
+    monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: True)
+    monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: True)
     call_command('cook', 'tests_schemas/recipe.json')
     assert len(fake_notify) == 3
 
 
 def test_cook_unresolvable(db, fake_notify, monkeypatch):
-    monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: False)
+    monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: False)
     with pytest.raises(CommandError) as e_info:
         call_command('cook', 'tests_schemas/recipe.json')
     assert 'is not resolvable' in str(e_info.value)
@@ -26,8 +26,8 @@ def test_cook_example(db, fake_notify, monkeypatch, fake_themes):
     """hobo/cook (before rabbitmq) scenario having templates.
     the resulting JSON may be helpfull to manually invoque hobo-deploy (after rabbitmq)
     """
-    monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: True)
-    monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: True)
+    monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: True)
+    monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: True)
     call_command('cook', 'tests_schemas/example_recipe.json')
 
     # notify_agents was call
diff --git a/tests_schemas/test_rename_hobo_service.py b/tests_schemas/test_rename_hobo_service.py
index 4e47b6e..6da1e64 100644
--- a/tests_schemas/test_rename_hobo_service.py
+++ b/tests_schemas/test_rename_hobo_service.py
@@ -5,15 +5,15 @@ from django.core.management import call_command
 from django.core.management.base import CommandError
 from tenant_schemas.utils import tenant_context
 
-from hobo.environment.models import Passerelle, ServiceBase
+from hobo.environment import models as environment_models
 from hobo.environment.utils import get_installed_services, get_or_create_local_hobo
 from hobo.multitenant.middleware import TenantMiddleware, TenantNotFound
 
 
 @pytest.fixture()
 def hobo_tenant(db, fake_notify, monkeypatch, fake_themes):
-    monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: True)
-    monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: True)
+    monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: True)
+    monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: True)
     yield call_command('cook', 'tests_schemas/example_recipe.json')
     call_command('delete_tenant', 'hobo-instance-name.dev.signalpublik.com')
 
@@ -34,8 +34,8 @@ def test_unknown_service(hobo_tenant):
 
 
 def test_rename_hobo_service_succes(db, fake_notify, monkeypatch, fake_themes):
-    monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: True)
-    monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: True)
+    monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: True)
+    monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: True)
     call_command('cook', 'tests_schemas/example_recipe.json')
     assert TenantMiddleware.get_tenant_by_hostname('hobo-instance-name.dev.signalpublik.com')
     call_command(
diff --git a/tests_schemas/test_rename_service.py b/tests_schemas/test_rename_service.py
index e91850d..7d2e88b 100644
--- a/tests_schemas/test_rename_service.py
+++ b/tests_schemas/test_rename_service.py
@@ -5,15 +5,15 @@ from django.core.management import call_command
 from django.core.management.base import CommandError
 from tenant_schemas.utils import tenant_context
 
-from hobo.environment.models import Passerelle, ServiceBase
+from hobo.environment import models as environment_models
 from hobo.environment.utils import get_installed_services
 from hobo.multitenant.middleware import TenantMiddleware
 
 
 @pytest.fixture()
 def hobo_tenant(db, fake_notify, monkeypatch, fake_themes):
-    monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: True)
-    monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: True)
+    monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: True)
+    monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: True)
     yield call_command('cook', 'tests_schemas/example_recipe.json')
     call_command('delete_tenant', 'hobo-instance-name.dev.signalpublik.com')
 
@@ -35,7 +35,7 @@ def test_secondary_service(hobo_tenant):
     tenant = TenantMiddleware.get_tenant_by_hostname('hobo-instance-name.dev.signalpublik.com')
     with tenant_context(tenant):
         assert get_installed_services()
-        Passerelle.objects.create(
+        environment_models.Passerelle.objects.create(
             title='other passerelle',
             slug='other-passerelle',
             base_url='https://other-passerelle-instance-name.dev.signalpublik.com',
@@ -56,8 +56,8 @@ def test_secondary_service(hobo_tenant):
 def test_rename_service_succes(hobo_tenant, monkeypatch):
     tenant = TenantMiddleware.get_tenant_by_hostname('hobo-instance-name.dev.signalpublik.com')
     with tenant_context(tenant):
-        assert Passerelle.objects.count() == 1
-        passerelle_service = Passerelle.objects.first()
+        assert environment_models.Passerelle.objects.count() == 1
+        passerelle_service = environment_models.Passerelle.objects.first()
         assert (
             passerelle_service.get_base_url_path() == 'https://passerelle-instance-name.dev.signalpublik.com/'
         )
@@ -69,8 +69,8 @@ def test_rename_service_succes(hobo_tenant, monkeypatch):
             'https://passerelle-instance-name.dev.signalpublik.com/',
             'https://new-passerelle-instance-name.dev.signalpublik.com/',
         )
-        assert Passerelle.objects.count() == 1
-        passerelle_service = Passerelle.objects.first()
+        assert environment_models.Passerelle.objects.count() == 1
+        passerelle_service = environment_models.Passerelle.objects.first()
         assert (
             passerelle_service.get_base_url_path()
             == 'https://new-passerelle-instance-name.dev.signalpublik.com/'
-- 
2.30.2

