compute_ctl: catalog API endpoints (#7575)

## Problem

There are two cloud's features that require extra compute endpoints.

1. We are running pg_dump to get DB schemas. Currently, we are using a
special service for this. But it would be great to execute pg_dump in an
isolated environment. And we already have such an environment, it's our
compute! And likely enough pg_dump already exists there too! (see
https://github.com/neondatabase/cloud/issues/11644#issuecomment-2084617832)
2. We need to have a way to get databases and roles from compute after
time travel (see https://github.com/neondatabase/cloud/issues/12109)

## Summary of changes

It adds two API endpoints to compute_ctl HTTP API that target both of
the aforementioned cases.

---------

Co-authored-by: Tristan Partin <tristan@neon.tech>
This commit is contained in:
Andrew Rudenko
2024-05-16 12:04:16 +02:00
committed by GitHub
parent 03c6039707
commit 923cf91aa4
11 changed files with 352 additions and 1 deletions

View File

@@ -0,0 +1,23 @@
import requests
from requests.adapters import HTTPAdapter
class EndpointHttpClient(requests.Session):
def __init__(
self,
port: int,
):
super().__init__()
self.port = port
self.mount("http://", HTTPAdapter())
def dbs_and_roles(self):
res = self.get(f"http://localhost:{self.port}/dbs_and_roles")
res.raise_for_status()
return res.json()
def database_schema(self, database: str):
res = self.get(f"http://localhost:{self.port}/database_schema?database={database}")
res.raise_for_status()
return res.text

View File

@@ -48,6 +48,7 @@ from urllib3.util.retry import Retry
from fixtures import overlayfs
from fixtures.broker import NeonBroker
from fixtures.common_types import Lsn, TenantId, TenantShardId, TimelineId
from fixtures.endpoint.http import EndpointHttpClient
from fixtures.log_helper import log
from fixtures.metrics import Metrics, MetricsGetter, parse_metrics
from fixtures.pageserver.allowed_errors import (
@@ -3373,6 +3374,13 @@ class Endpoint(PgProtocol):
self.active_safekeepers: List[int] = list(map(lambda sk: sk.id, env.safekeepers))
# path to conf is <repo_dir>/endpoints/<endpoint_id>/pgdata/postgresql.conf
def http_client(
self, auth_token: Optional[str] = None, retries: Optional[Retry] = None
) -> EndpointHttpClient:
return EndpointHttpClient(
port=self.http_port,
)
def create(
self,
branch_name: str,

View File

@@ -0,0 +1,34 @@
import requests
from fixtures.neon_fixtures import NeonEnv
def test_compute_catalog(neon_simple_env: NeonEnv):
env = neon_simple_env
env.neon_cli.create_branch("test_config", "empty")
endpoint = env.endpoints.create_start("test_config", config_lines=["log_min_messages=debug1"])
client = endpoint.http_client()
objects = client.dbs_and_roles()
# Assert that 'cloud_admin' role exists in the 'roles' list
assert any(
role["name"] == "cloud_admin" for role in objects["roles"]
), "The 'cloud_admin' role is missing"
# Assert that 'postgres' database exists in the 'databases' list
assert any(
db["name"] == "postgres" for db in objects["databases"]
), "The 'postgres' database is missing"
ddl = client.database_schema(database="postgres")
assert "-- PostgreSQL database dump" in ddl
try:
client.database_schema(database="nonexistentdb")
raise AssertionError("Expected HTTPError was not raised")
except requests.exceptions.HTTPError as e:
assert (
e.response.status_code == 404
), f"Expected 404 status code, but got {e.response.status_code}"