Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tests/model_registry/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class ModelRegistryEndpoints:
PORT_MAP = {
"mariadb": 3306,
"mysql": 3306,
"postgres": 5432,
}
MODEL_REGISTRY_POD_FILTER: str = "component=model-registry"
DEFAULT_CUSTOM_MODEL_CATALOG: str = "model-catalog-sources"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
get_model_registry_db_label_dict,
get_model_registry_deployment_template_dict,
get_mr_standard_labels,
get_mysql_config,
get_external_db_config,
)

ns_name = py_config["model_registry_namespace"]
Expand Down Expand Up @@ -79,7 +79,9 @@
"namespace": ns_name,
"label": get_mr_standard_labels(resource_name=f"{MR_INSTANCE_BASE_NAME}{index}"),
"rest": {},
"mysql": get_mysql_config(base_name=f"{DB_BASE_RESOURCES_NAME}{index}", namespace=ns_name, db_backend="mysql"),
"mysql": get_external_db_config(
base_name=f"{DB_BASE_RESOURCES_NAME}{index}", namespace=ns_name, db_backend="mysql"
),
"wait_for_resource": True,
"kube_rbac_proxy": {},
}
Expand Down
146 changes: 88 additions & 58 deletions tests/model_registry/model_registry/rest_api/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
from tests.model_registry.utils import (
get_model_registry_deployment_template_dict,
apply_mysql_args_and_volume_mounts,
add_mysql_certs_volumes_to_deployment,
add_db_certs_volumes_to_deployment,
get_mr_standard_labels,
get_mysql_config,
get_external_db_config,
)

from tests.model_registry.constants import (
Expand Down Expand Up @@ -119,48 +119,87 @@ def patch_invalid_ca(


@pytest.fixture(scope="class")
def mysql_template_with_ca(model_registry_metadata_db_resources: dict[Any, Any]) -> dict[str, Any]:
def db_backend_under_test(request: pytest.FixtureRequest) -> str:
"""
Patches the MySQL template with the CA file path and volume mount.
Fixture to provide the database backend type being tested.

Args:
request: The pytest request object containing test parameters

Returns:
str: The database backend type (e.g., "mysql", "postgres")
"""
return request.param


@pytest.fixture(scope="class")
def external_db_template_with_ca(
model_registry_metadata_db_resources: dict[Any, Any], db_backend_under_test: str
) -> dict[str, Any]:
"""
mysql_template = get_model_registry_deployment_template_dict(
Patches the external database template with the CA file path and volume mount.
"""
db_deployment_template = get_model_registry_deployment_template_dict(
secret_name=model_registry_metadata_db_resources[Secret][0].name,
resource_name=DB_RESOURCE_NAME,
db_backend="mysql",
db_backend=db_backend_under_test,
)
mysql_template["spec"]["containers"][0]["args"].append(f"--ssl-ca={CA_FILE_PATH}")
mysql_template["spec"]["containers"][0]["volumeMounts"].append({

if db_backend_under_test == "mysql":
db_deployment_template["spec"]["containers"][0]["args"].append(f"--ssl-ca={CA_FILE_PATH}")

elif db_backend_under_test == "postgres":
postgres_ssl_args = [
"postgres",
"-c",
"ssl=on",
"-c",
"ssl_cert_file=/etc/server-cert/tls.crt",
"-c",
"ssl_key_file=/etc/server-cert/tls.key",
"-c",
"ssl_ca_file=/etc/server-cert/ca.crt",
Comment thread
fege marked this conversation as resolved.
Outdated
]
db_deployment_template["spec"]["containers"][0].get("args", []).extend(postgres_ssl_args)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
db_deployment_template["spec"]["containers"][0]["volumeMounts"].append({
"mountPath": CA_MOUNT_PATH,
"name": CA_CONFIGMAP_NAME,
"readOnly": True,
})
mysql_template["spec"]["volumes"].append({"name": CA_CONFIGMAP_NAME, "configMap": {"name": CA_CONFIGMAP_NAME}})
return mysql_template
db_deployment_template["spec"]["volumes"].append({
"name": CA_CONFIGMAP_NAME,
"configMap": {"name": CA_CONFIGMAP_NAME},
})
return db_deployment_template
Comment thread
dbasunag marked this conversation as resolved.


@pytest.fixture(scope="class")
def deploy_secure_mysql_and_mr(
def deploy_secure_db_mr(
request: pytest.FixtureRequest,
admin_client: DynamicClient,
model_registry_namespace: str,
mysql_template_with_ca: dict[str, Any],
patch_mysql_deployment_with_ssl_ca: Deployment,
external_db_template_with_ca: dict[str, Any],
patch_external_deployment_with_ssl_ca: Deployment,
db_backend_under_test: str,
) -> Generator[ModelRegistry, None, None]:
"""
Deploy a secure MySQL and Model Registry instance.
Deploy a secure database and Model Registry instance.
"""
param = getattr(request, "param", {})
mysql = get_mysql_config(base_name=DB_RESOURCE_NAME, namespace=model_registry_namespace, db_backend="mysql")
db_config = get_external_db_config(
base_name=DB_RESOURCE_NAME, namespace=model_registry_namespace, db_backend=db_backend_under_test
)
if "sslRootCertificateConfigMap" in param:
mysql["sslRootCertificateConfigMap"] = param["sslRootCertificateConfigMap"]
db_config["sslRootCertificateConfigMap"] = param["sslRootCertificateConfigMap"]
with ModelRegistry(
client=admin_client,
name=SECURE_MR_NAME,
namespace=model_registry_namespace,
label=get_mr_standard_labels(resource_name=SECURE_MR_NAME),
rest={},
kube_rbac_proxy={},
mysql=mysql,
mysql=db_config if db_backend_under_test == "mysql" else None,
postgres=db_config if db_backend_under_test == "postgres" else None,
Comment thread
dbasunag marked this conversation as resolved.
wait_for_resource=True,
) as mr:
mr.wait_for_condition(condition="Available", status="True")
Expand Down Expand Up @@ -197,25 +236,25 @@ def local_ca_bundle(request: pytest.FixtureRequest, admin_client: DynamicClient)
def ca_configmap_for_test(
admin_client: DynamicClient,
model_registry_namespace: str,
mysql_ssl_artifact_paths: dict[str, Any],
external_db_ssl_artifact_paths: dict[str, Any],
) -> Generator[ConfigMap, None, None]:
"""
Creates a test-specific ConfigMap for the CA bundle, using the generated CA cert.

Args:
admin_client: The admin client to create the ConfigMap
model_registry_namespace: The namespace of the model registry
mysql_ssl_artifact_paths: The artifacts and secrets for the MySQL SSL connection
external_db_ssl_artifact_paths: The artifacts and secrets for the external database SSL connection

Returns:
Generator[ConfigMap, None, None]: A generator that yields the ConfigMap instance.
"""
with open(mysql_ssl_artifact_paths["ca_crt"], "r") as f:
with open(external_db_ssl_artifact_paths["ca_crt"], "r") as f:
ca_content = f.read()
if not ca_content:
LOGGER.info("CA content is empty")
raise Exception("CA content is empty")
cm_name = "mysql-ca-configmap"
cm_name = "db-ca-configmap"
with ConfigMap(
client=admin_client,
name=cm_name,
Expand All @@ -226,34 +265,40 @@ def ca_configmap_for_test(


@pytest.fixture(scope="class")
def patch_mysql_deployment_with_ssl_ca(
def patch_external_deployment_with_ssl_ca(
request: pytest.FixtureRequest,
admin_client: DynamicClient,
model_registry_namespace: str,
mysql_ssl_secrets: dict[str, Any],
external_db_ssl_secrets: dict[str, Any],
db_backend_under_test: str,
) -> Generator[Deployment, Any, Any]:
"""
Patch the MySQL deployment to use the test CA bundle (mysql-ca-configmap),
Patch the external database deployment to use the test CA bundle,
and mount the server cert/key for SSL.
"""
model_registry_db_deployments = get_mr_deployment(admin_client=admin_client, mr_namespace=model_registry_namespace)
if request.param.get("ca_configmap_for_test"):
LOGGER.info("Invoking ca_configmap_for_test fixture")
request.getfixturevalue(argname="ca_configmap_for_test")
CA_CONFIGMAP_NAME = request.param.get("ca_configmap_name", "mysql-ca-configmap")
CA_CONFIGMAP_NAME = request.param.get("ca_configmap_name", "db-ca-configmap")
CA_MOUNT_PATH = request.param.get("ca_mount_path", "/etc/mysql/ssl")
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

deployment = model_registry_db_deployments[0].instance.to_dict()
spec = deployment["spec"]["template"]["spec"]
my_sql_container = next(container for container in spec["containers"] if container["name"] == "mysql")
assert my_sql_container is not None, "Mysql container not found"

my_sql_container = apply_mysql_args_and_volume_mounts(
my_sql_container=my_sql_container, ca_configmap_name=CA_CONFIGMAP_NAME, ca_mount_path=CA_MOUNT_PATH
db_container = next(container for container in spec["containers"] if container["name"] == db_backend_under_test)
assert db_container is not None, f"{db_backend_under_test} container not found"
Comment thread
dbasunag marked this conversation as resolved.
Outdated

db_container = apply_mysql_args_and_volume_mounts(
db_container=db_container,
ca_configmap_name=CA_CONFIGMAP_NAME,
ca_mount_path=CA_MOUNT_PATH,
db_backend=db_backend_under_test,
)
volumes = add_db_certs_volumes_to_deployment(
spec=spec, ca_configmap_name=CA_CONFIGMAP_NAME, db_backend=db_backend_under_test
)
volumes = add_mysql_certs_volumes_to_deployment(spec=spec, ca_configmap_name=CA_CONFIGMAP_NAME)

patch = {"spec": {"template": {"spec": {"volumes": volumes, "containers": [my_sql_container]}}}}
patch = {"spec": {"template": {"spec": {"volumes": volumes, "containers": [db_container]}}}}
with ResourceEditor(patches={model_registry_db_deployments[0]: patch}):
wait_for_pods_running(
admin_client=admin_client, namespace_name=model_registry_namespace, number_of_consecutive_checks=3
Expand All @@ -263,59 +308,44 @@ def patch_mysql_deployment_with_ssl_ca(


@pytest.fixture(scope="class")
def mysql_ssl_artifact_paths() -> Generator[dict[str, str], None, None]:
def external_db_ssl_artifact_paths() -> Generator[dict[str, str], None, None]:
"""
Generates MySQL SSL certificate and key files in a temporary directory
Generates external database SSL certificate and key files in a temporary directory
and provides their paths.

Args:
admin_client: The admin client to create the ConfigMap
model_registry_namespace: The namespace of the model registry
mysql_ssl_artifacts_and_secrets: The artifacts and secrets for the MySQL SSL connection

Returns:
Generator[dict[str, str], None, None]: A generator that yields the CA certificate and key file paths.
"""
with tempfile.TemporaryDirectory() as tmp_dir:
yield generate_ca_and_server_cert(tmp_dir=tmp_dir)


@pytest.fixture(scope="class")
def mysql_ssl_secrets(
def external_db_ssl_secrets(
admin_client: DynamicClient,
model_registry_namespace: str,
mysql_ssl_artifact_paths: dict[str, str],
external_db_ssl_artifact_paths: dict[str, str],
) -> Generator[dict[str, Secret], None, None]:
"""
Creates Kubernetes secrets for MySQL SSL artifacts.

Args:
admin_client: The admin client to create the ConfigMap
model_registry_namespace: The namespace of the model registry
mysql_ssl_artifacts_and_secrets: The artifacts and secrets for the MySQL SSL connection

Returns:
Generator[dict[str, str], None, None]: A generator that yields the CA certificate and key file paths.
Comment thread
fege marked this conversation as resolved.
Creates Kubernetes secrets for external database SSL artifacts.
"""
ca_secret = create_k8s_secret(
client=admin_client,
namespace=model_registry_namespace,
name="mysql-ca",
file_path=mysql_ssl_artifact_paths["ca_crt"],
name="db-ca",
file_path=external_db_ssl_artifact_paths["ca_crt"],
key_name="ca.crt",
)
server_cert_secret = create_k8s_secret(
client=admin_client,
namespace=model_registry_namespace,
name="mysql-server-cert",
file_path=mysql_ssl_artifact_paths["server_crt"],
name="db-server-cert",
file_path=external_db_ssl_artifact_paths["server_crt"],
key_name="tls.crt",
)
server_key_secret = create_k8s_secret(
client=admin_client,
namespace=model_registry_namespace,
name="mysql-server-key",
file_path=mysql_ssl_artifact_paths["server_key"],
name="db-server-key",
file_path=external_db_ssl_artifact_paths["server_key"],
key_name="tls.key",
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,18 @@
marks=(pytest.mark.smoke),
),
pytest.param(
{"db_name": "mariadb"},
{"db_name": "mariadb"},
{"db_name": "postgres"},
{"db_name": "postgres"},
MODEL_REGISTER_DATA,
marks=(pytest.mark.sanity),
marks=(pytest.mark.smoke),
),
pytest.param(
{"db_name": "default"},
{"db_name": "default"},
{"db_name": "mariadb"},
{"db_name": "mariadb"},
MODEL_REGISTER_DATA,
marks=(pytest.mark.sanity),
),
pytest.param({"db_name": "default"}, {"db_name": "default"}, MODEL_REGISTER_DATA, marks=(pytest.mark.smoke)),
],
indirect=True,
)
Expand Down
Loading