Repository: pulp/pulpcore

Issue:
evr="" is sent to the database on creating a Package
**Version**
current main branches of everything.

**Describe the bug**
It looks like the evr field is translated into the insert query even if it was not set from the validated_data. Also it seems if should be populated by a database trigger and never be saved directly.

```
[('INSERT INTO "rpm_package" ("content_ptr_id", "name", "epoch", "version", "release", "arch", "evr", "pkgId", "checksum_type", "summary", "description", "url", "changelogs", "files", "requires", "provides", "conflicts", "obsoletes", "suggests", "enhances", "recommends", "supplements", "location_base", "location_href", "rpm_buildhost", "rpm_group", "rpm_license", "rpm_packager", "rpm_sourcerpm", "rpm_vendor", "rpm_header_start", "rpm_header_end", "size_archive", "size_installed", "size_package", "time_build", "time_file", "is_modular") VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)', (UUID('ff1f1dd3-d4bf-4104-a2ea-7baed11dc6e8'), 'lemon', '0', '0', '1', 'noarch', '', '847df5d46b4d8ab2a7af5bfc0ccfb4681786a2b40b0f3a2b9a56bbed8dfeeee6', 'sha256', 'A lemon', '', 'http://lem.on/Fruits/', '[]', '[]', '[]', '[["citrussy", null, null, null, null, false], ["fruity", null, null, null, null, false], ["lemon", null, null, null, null, false], ["lemon", "EQ", "0", "0", "1", false]]', '[]', '[]', '[]', '[]', '[]', '[]', '', 'lemon-0-1.noarch.rpm', '68aa50d573f8', 'Fruits', 'No Age Limitation', 'Mr. Bartender', 'lemon-0-1.src.rpm', 'Lem:ON!', 4504, 5924, 124, 0, 6040, 1657826273, 1661253000, False))]
```

**To Reproduce**
run the pulp-cli test for rpm content
```
pytest -m pulp_rpm -k content
```

**Expected behavior**
test should pass

**Additional context**
Add any other context about the problem here.  Please provide links to any previous discussions via Discourse or Bugzilla.



Discussion:
This turned out to be a pulpcore issue.

In pulpcore we start doing orm calls before all apps are ready. This results in django-readonly-fields doing its ninja-move to swap out the SQL Compiler before django computes it and caches it in https://github.com/django/django/blob/main/django/db/backends/base/operations.py#L361 .

Lesson learned: Do not touch the orm before everything is ready.

Plan to resolve: Move the installation of the telemetry schedule into a new worker-startup hook.

Resolving patch:
diff --git a/pulpcore/app/apps.py b/pulpcore/app/apps.py
--- a/pulpcore/app/apps.py
+++ b/pulpcore/app/apps.py
@@ -1,7 +1,6 @@
 from collections import defaultdict
 from gettext import gettext as _
 from importlib import import_module
-from datetime import timedelta
 
 from django import apps
 from django.core.exceptions import ImproperlyConfigured
@@ -212,8 +211,6 @@ def ready(self):
         super().ready()
         from . import checks  # noqa
 
-        _configure_telemetry(self.apps)
-
         post_migrate.connect(
             _populate_system_id, sender=self, dispatch_uid="populate_system_id_identifier"
         )
@@ -262,26 +259,6 @@ def _populate_system_id(sender, apps, verbosity, **kwargs):
         SystemID().save()
 
 
-def _configure_telemetry(apps):
-    from django.db import connection
-    from pulpcore.app.util import get_telemetry_posting_url, PRODUCTION_URL
-
-    if "core_taskschedule" in connection.introspection.table_names():
-        url = get_telemetry_posting_url()
-        TaskSchedule = apps.get_model("core", "TaskSchedule")
-        task_name = "pulpcore.app.tasks.telemetry.post_telemetry"
-        dispatch_interval = timedelta(days=1)
-        name = "Post Anonymous Telemetry Periodically"
-        # Initially only dev systems receive posted data.
-        if url == PRODUCTION_URL:
-            TaskSchedule.objects.filter(task_name=task_name).delete()
-        else:
-            TaskSchedule.objects.update_or_create(
-                name=name, defaults={"task_name": task_name, "dispatch_interval": dispatch_interval}
-            )
-    connection.close()
-
-
 def _populate_roles(sender, apps, verbosity, **kwargs):
     role_prefix = f"{sender.label}."
     # collect all plugin defined roles
diff --git a/pulpcore/app/util.py b/pulpcore/app/util.py
--- a/pulpcore/app/util.py
+++ b/pulpcore/app/util.py
@@ -1,6 +1,7 @@
 import os
 import tempfile
 
+from datetime import timedelta
 import gnupg
 
 from django.conf import settings
@@ -215,3 +216,17 @@ def get_telemetry_posting_url():
             return DEV_URL
 
     return PRODUCTION_URL
+
+
+def configure_telemetry():
+    url = get_telemetry_posting_url()
+    task_name = "pulpcore.app.tasks.telemetry.post_telemetry"
+    dispatch_interval = timedelta(days=1)
+    name = "Post Anonymous Telemetry Periodically"
+    # Initially only dev systems send data.
+    if url == PRODUCTION_URL:
+        models.TaskSchedule.objects.filter(task_name=task_name).delete()
+    else:
+        models.TaskSchedule.objects.update_or_create(
+            name=name, defaults={"task_name": task_name, "dispatch_interval": dispatch_interval}
+        )
diff --git a/pulpcore/tasking/pulpcore_worker.py b/pulpcore/tasking/pulpcore_worker.py
--- a/pulpcore/tasking/pulpcore_worker.py
+++ b/pulpcore/tasking/pulpcore_worker.py
@@ -33,6 +33,7 @@
 
 from pulpcore.app.models import Worker, Task  # noqa: E402: module level not at top of file
 
+from pulpcore.app.util import configure_telemetry  # noqa: E402: module level not at top of file
 from pulpcore.app.role_util import (  # noqa: E402: module level not at top of file
     get_users_with_perms,
 )
@@ -58,6 +59,10 @@
 TASK_SCHEDULING_LOCK = 42
 
 
+def startup_hook():
+    configure_telemetry()
+
+
 class PGAdvisoryLock:
     """
     A context manager that will hold a postgres advisory lock non-blocking.
@@ -132,6 +137,8 @@ def __init__(self):
         os.set_blocking(sentinel_w, False)
         signal.set_wakeup_fd(sentinel_w)
 
+        startup_hook()
+
     def _signal_handler(self, thesignal, frame):
         # Reset signal handlers to default
         # If you kill the process a second time it's not graceful anymore.


Test patch: