Repository: pulp/pulpcore

Issue:
Have a lean way to check for the health of a pulp instance
**Is your feature request related to a problem? Please describe.**
Right now when we want to check for how healthy is a pulp instance we hit the `/status/` endpoint which returns:
- The pulpcore version
- plugins installed and its versions
- space available
- number of api/content/workers
- db/redis connection
- domain enabled
- and so on...

Although this information is really useful when you're interacting with a pulp instance, for automated systems that
need to check it from time to time this doesn't seems to be need or even wanted.

**Describe the solution you'd like**
What if Pulp could be checked with just a HEAD request? 200 if a minimum number of workers are online and a 400 class if something is wrong?
No body on the response. No need to serialize anything.
Just the minimal amount of things needed to understand that Pulp is alive and kicking.

**Describe alternatives you've considered**
We could have the HEAD method on the status endpoint itself.
HEAD if you just wanted a lean check, and GET if you need all possible data from your instance.

This example if just to provoke the discussion:
```python
    def head(self, *args):
        if settings.CACHE_ENABLED:
            redis_status = self._get_redis_conn_status()
        else:
            redis_status = False

        db_status = self._get_db_conn_status()

        online_workers = Worker.objects.online().exists()
        online_api_apps = ApiAppStatus.objects.online().exists()
        online_content_apps = ContentAppStatus.objects.online().exists()

        healthy_stack = all(
            [online_content_apps, online_api_apps, online_workers, db_status, redis_status]
        )

        if healthy_stack:
            return Response(status=200)

        return Response(status=409)
```


Discussion:
What if for the HEAD request we just return 200 OK without doing anything? That's as light weight as it gets.
> What if for the HEAD request we just return 200 OK without doing anything? That's as light weight as it gets.

Neat, but dangerous. What is an API without database access?
Can we say it's really alive?
I just tried this out. I modified the status viewset to just return `Response("ok")` and when I take down the database I get a 500 error back. I also see the following in the logs:

```
pulp [d6479c3347714d5390c54a7f327410df]: django.request:ERROR: Internal Server Error: /pulp/api/v3/status/
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/base.py", line 289, in ensure_connection
    self.connect()
  File "/usr/local/lib/python3.9/site-packages/django/utils/asyncio.py", line 26, in inner
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/base.py", line 270, in connect
    self.connection = self.get_new_connection(conn_params)
  File "/usr/local/lib/python3.9/site-packages/django/utils/asyncio.py", line 26, in inner
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/postgresql/base.py", line 275, in get_new_connection
    connection = self.Database.connect(**conn_params)
  File "/usr/local/lib/python3.9/site-packages/psycopg/connection.py", line 748, in connect
    raise last_ex.with_traceback(None)
psycopg.OperationalError: connection is bad: No such file or directory
        Is the server running locally and accepting connections on that socket?
```

So I think we can just rely on Django using the database when handling the request to know that the database connection is working.
I think you can use the fields query parameter to get less information.
Reverted the initial feature implementation because it was not compatible with Kubernetes livenessProbe. 
Let's add a new API endpoint at `/pulp/api/v3/livez` that will respond with 200 OK and an empty response body.

Resolving patch:
diff --git a/pulpcore/app/urls.py b/pulpcore/app/urls.py
--- a/pulpcore/app/urls.py
+++ b/pulpcore/app/urls.py
@@ -13,7 +13,13 @@
 from rest_framework.routers import APIRootView
 
 from pulpcore.app.apps import pulp_plugin_configs
-from pulpcore.app.views import OrphansView, PulpImporterImportCheckView, RepairView, StatusView
+from pulpcore.app.views import (
+    LivezView,
+    OrphansView,
+    PulpImporterImportCheckView,
+    RepairView,
+    StatusView,
+)
 from pulpcore.app.viewsets import (
     ListRepositoryVersionViewSet,
     OrphansCleanupViewset,
@@ -157,6 +163,7 @@ class PulpDefaultRouter(routers.DefaultRouter):
 ]
 
 docs_and_status = [
+    path("livez/", LivezView.as_view()),
     path("status/", StatusView.as_view()),
     path(
         "docs/api.json",
diff --git a/pulpcore/app/views/__init__.py b/pulpcore/app/views/__init__.py
--- a/pulpcore/app/views/__init__.py
+++ b/pulpcore/app/views/__init__.py
@@ -1,4 +1,4 @@
 from .orphans import OrphansView
-from .status import StatusView
+from .status import LivezView, StatusView
 from .repair import RepairView
 from .importer import PulpImporterImportCheckView
diff --git a/pulpcore/app/views/status.py b/pulpcore/app/views/status.py
--- a/pulpcore/app/views/status.py
+++ b/pulpcore/app/views/status.py
@@ -136,3 +136,24 @@ def _get_redis_conn_status():
             return False
         else:
             return True
+
+
+class LivezView(APIView):
+    """
+    Liveness Probe for the REST API.
+    """
+
+    # allow anyone to access the liveness api
+    authentication_classes = []
+    permission_classes = []
+
+    @extend_schema(
+        summary="Inspect liveness of Pulp's REST API.",
+        operation_id="livez_read",
+        responses={200: None},
+    )
+    def get(self, request):
+        """
+        Returns 200 OK when API is alive.
+        """
+        return Response()


Test patch:
diff --git a/pulpcore/tests/functional/api/test_status.py b/pulpcore/tests/functional/api/test_status.py
--- a/pulpcore/tests/functional/api/test_status.py
+++ b/pulpcore/tests/functional/api/test_status.py
@@ -175,3 +175,15 @@ def verify_get_response(status, expected_schema):
     else:
         assert status["storage"]["free"] is not None
         assert status["storage"]["total"] is not None
+
+
+@pytest.mark.parallel
+def test_livez_unauthenticated(
+    pulpcore_bindings,
+    anonymous_user,
+):
+    """
+    Assert that GET requests to Livez API return 200 without a response body.
+    """
+    with anonymous_user:
+        assert pulpcore_bindings.LivezApi.livez_read() is None