-
Notifications
You must be signed in to change notification settings - Fork 61
Updates for GA of Model Registry #571
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
466cd94
25e172e
e91d07e
961d507
f73e459
849762e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -52,7 +52,7 @@ | |||||||||||||||||||||||||||||||||||
| from utilities.constants import DscComponents | ||||||||||||||||||||||||||||||||||||
| from model_registry import ModelRegistry as ModelRegistryClient | ||||||||||||||||||||||||||||||||||||
| from utilities.general import wait_for_pods_by_labels | ||||||||||||||||||||||||||||||||||||
| from utilities.infra import get_data_science_cluster | ||||||||||||||||||||||||||||||||||||
| from utilities.infra import get_data_science_cluster, wait_for_dsc_status_ready | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| DEFAULT_TOKEN_DURATION = "10m" | ||||||||||||||||||||||||||||||||||||
| LOGGER = get_logger(name=__name__) | ||||||||||||||||||||||||||||||||||||
|
|
@@ -169,57 +169,73 @@ def updated_dsc_component_state_scope_session( | |||||||||||||||||||||||||||||||||||
| pytestconfig: Config, | ||||||||||||||||||||||||||||||||||||
| request: FixtureRequest, | ||||||||||||||||||||||||||||||||||||
| admin_client: DynamicClient, | ||||||||||||||||||||||||||||||||||||
| teardown_resources: bool, | ||||||||||||||||||||||||||||||||||||
| ) -> Generator[DataScienceCluster, Any, Any]: | ||||||||||||||||||||||||||||||||||||
| dsc_resource = get_data_science_cluster(client=admin_client) | ||||||||||||||||||||||||||||||||||||
| if not teardown_resources or pytestconfig.option.post_upgrade: | ||||||||||||||||||||||||||||||||||||
| # if we are not tearing down resources or we are in post upgrade, we don't need to do anything | ||||||||||||||||||||||||||||||||||||
| # the pre_upgrade/post_upgrade fixtures will handle the rest | ||||||||||||||||||||||||||||||||||||
| yield dsc_resource | ||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||
| original_components = dsc_resource.instance.spec.components | ||||||||||||||||||||||||||||||||||||
| component_patch = { | ||||||||||||||||||||||||||||||||||||
| DscComponents.MODELREGISTRY: { | ||||||||||||||||||||||||||||||||||||
| "managementState": DscComponents.ManagementState.MANAGED, | ||||||||||||||||||||||||||||||||||||
| "registriesNamespace": py_config["model_registry_namespace"], | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| LOGGER.info(f"Applying patch {component_patch}") | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| with ResourceEditor(patches={dsc_resource: {"spec": {"components": component_patch}}}): | ||||||||||||||||||||||||||||||||||||
| for component_name in component_patch: | ||||||||||||||||||||||||||||||||||||
| dsc_resource.wait_for_condition( | ||||||||||||||||||||||||||||||||||||
| condition=DscComponents.COMPONENT_MAPPING[component_name], status="True" | ||||||||||||||||||||||||||||||||||||
| original_namespace_name = dsc_resource.instance.spec.components.modelregistry.registriesNamespace | ||||||||||||||||||||||||||||||||||||
| if pytestconfig.option.custom_namespace: | ||||||||||||||||||||||||||||||||||||
| resource_editor = ResourceEditor( | ||||||||||||||||||||||||||||||||||||
| patches={ | ||||||||||||||||||||||||||||||||||||
| dsc_resource: { | ||||||||||||||||||||||||||||||||||||
| "spec": { | ||||||||||||||||||||||||||||||||||||
| "components": { | ||||||||||||||||||||||||||||||||||||
| DscComponents.MODELREGISTRY: { | ||||||||||||||||||||||||||||||||||||
| "managementState": DscComponents.ManagementState.REMOVED, | ||||||||||||||||||||||||||||||||||||
| "registriesNamespace": original_namespace_name, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||
| # first disable MR | ||||||||||||||||||||||||||||||||||||
| resource_editor.update(backup_resources=True) | ||||||||||||||||||||||||||||||||||||
| wait_for_dsc_status_ready(dsc_resource=dsc_resource) | ||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace single-shot DSC readiness check with retry loop Prevents transient failures after patching DSC. - wait_for_dsc_status_ready(dsc_resource=dsc_resource)
+ # Wait for DSC to reconcile after disabling MR (retry to avoid flakes)
+ for _ in range(60): # ~5 minutes
+ try:
+ wait_for_dsc_status_ready(dsc_resource=dsc_resource)
+ break
+ except Exception:
+ time.sleep(5)
+ else:
+ # Final raise with the same helper for context
+ wait_for_dsc_status_ready(dsc_resource=dsc_resource)Add once near the imports: import time🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
| # now delete the original namespace: | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+191
to
+194
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. “wait_for_dsc_status_ready” is a single-shot check, not a wait — high flake risk. This helper returns immediately and raises if not yet Ready. After patching DSC, readiness can take time; a single check will intermittently fail. Recommended: replace this call with a real poll here, or update utilities.infra.wait_for_dsc_status_ready to poll with a timeout. If changing here, for example: - resource_editor.update(backup_resources=True)
- wait_for_dsc_status_ready(dsc_resource=dsc_resource)
+ resource_editor.update(backup_resources=True)
+ # Poll for DSC readiness to avoid flakes
+ for _ in range(36): # ~3 minutes @5s
+ try:
+ wait_for_dsc_status_ready(dsc_resource=dsc_resource)
+ break
+ except Exception:
+ import time; time.sleep(5)
+ else:
+ # Final raise with context
+ wait_for_dsc_status_ready(dsc_resource=dsc_resource)Alternatively, upgrade the util to actually wait and keep the single call here. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
| original_namespace = Namespace(name=original_namespace_name, wait_for_resource=True) | ||||||||||||||||||||||||||||||||||||
| original_namespace.delete(wait=True) | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+195
to
+196
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we actually need to delete it? it could be left there since we are creating it again after a few lines
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cleaned it that way there is no confusion about leftover resources. |
||||||||||||||||||||||||||||||||||||
| # Now enable it with the custom namespace | ||||||||||||||||||||||||||||||||||||
| with ResourceEditor( | ||||||||||||||||||||||||||||||||||||
| patches={ | ||||||||||||||||||||||||||||||||||||
| dsc_resource: { | ||||||||||||||||||||||||||||||||||||
| "spec": { | ||||||||||||||||||||||||||||||||||||
| "components": { | ||||||||||||||||||||||||||||||||||||
| DscComponents.MODELREGISTRY: { | ||||||||||||||||||||||||||||||||||||
| "managementState": DscComponents.ManagementState.MANAGED, | ||||||||||||||||||||||||||||||||||||
| "registriesNamespace": py_config["model_registry_namespace"], | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||
| namespace = Namespace(name=py_config["model_registry_namespace"], wait_for_resource=True) | ||||||||||||||||||||||||||||||||||||
| namespace.wait_for_status(status=Namespace.Status.ACTIVE) | ||||||||||||||||||||||||||||||||||||
| wait_for_pods_running( | ||||||||||||||||||||||||||||||||||||
| admin_client=admin_client, | ||||||||||||||||||||||||||||||||||||
| namespace_name=py_config["applications_namespace"], | ||||||||||||||||||||||||||||||||||||
| number_of_consecutive_checks=6, | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| wait_for_pods_running( | ||||||||||||||||||||||||||||||||||||
| admin_client=admin_client, | ||||||||||||||||||||||||||||||||||||
| namespace_name=py_config["model_registry_namespace"], | ||||||||||||||||||||||||||||||||||||
| number_of_consecutive_checks=6, | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| namespace = Namespace(name=py_config["model_registry_namespace"], wait_for_resource=True) | ||||||||||||||||||||||||||||||||||||
| namespace.wait_for_status(status=Namespace.Status.ACTIVE) | ||||||||||||||||||||||||||||||||||||
| yield dsc_resource | ||||||||||||||||||||||||||||||||||||
| finally: | ||||||||||||||||||||||||||||||||||||
| resource_editor.restore() | ||||||||||||||||||||||||||||||||||||
| Namespace(name=py_config["model_registry_namespace"]).delete(wait=True) | ||||||||||||||||||||||||||||||||||||
| # create the original namespace object again, so that we can wait for it to be created first | ||||||||||||||||||||||||||||||||||||
| original_namespace = Namespace(name=original_namespace_name, wait_for_resource=True) | ||||||||||||||||||||||||||||||||||||
| original_namespace.wait_for_status(status=Namespace.Status.ACTIVE) | ||||||||||||||||||||||||||||||||||||
| wait_for_pods_running( | ||||||||||||||||||||||||||||||||||||
| admin_client=admin_client, | ||||||||||||||||||||||||||||||||||||
| namespace_name=py_config["applications_namespace"], | ||||||||||||||||||||||||||||||||||||
| number_of_consecutive_checks=6, | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+225
to
235
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Restore order: recreate original NS before restoring DSC; add readiness + MR-NS pod waits Current flow restores DSC while the original namespace may not exist, causing noisy reconciliation. Also, you only wait for pods in the applications namespace on restore. - yield dsc_resource
- finally:
- resource_editor.restore()
- Namespace(name=py_config["model_registry_namespace"]).delete(wait=True)
- # create the original namespace object again, so that we can wait for it to be created first
- original_namespace = Namespace(name=original_namespace_name, wait_for_resource=True)
- original_namespace.wait_for_status(status=Namespace.Status.ACTIVE)
- wait_for_pods_running(
- admin_client=admin_client,
- namespace_name=py_config["applications_namespace"],
- number_of_consecutive_checks=6,
- )
+ yield dsc_resource
+ finally:
+ # First, delete the custom namespace while MR is still Removed (after exiting the inner editor)
+ if py_config["model_registry_namespace"] != original_namespace_name:
+ Namespace(name=py_config["model_registry_namespace"]).delete(wait=True)
+ # Ensure the original namespace exists before pointing DSC back to it
+ original_namespace = Namespace(name=original_namespace_name, wait_for_resource=True)
+ original_namespace.wait_for_status(status=Namespace.Status.ACTIVE)
+ # Restore DSC to its original spec (likely Managed in the original namespace)
+ resource_editor.restore()
+ # Wait for DSC to become READY again (retry to avoid flakes)
+ for _ in range(60):
+ try:
+ wait_for_dsc_status_ready(dsc_resource=dsc_resource)
+ break
+ except Exception:
+ time.sleep(5)
+ else:
+ wait_for_dsc_status_ready(dsc_resource=dsc_resource)
+ # Wait for pods to settle in both app and MR namespaces
+ wait_for_pods_running(
+ admin_client=admin_client,
+ namespace_name=py_config["applications_namespace"],
+ number_of_consecutive_checks=6,
+ )
+ wait_for_pods_running(
+ admin_client=admin_client,
+ namespace_name=original_namespace_name,
+ number_of_consecutive_checks=6,
+ )
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
| wait_for_pods_running( | ||||||||||||||||||||||||||||||||||||
| admin_client=admin_client, | ||||||||||||||||||||||||||||||||||||
| namespace_name=py_config["model_registry_namespace"], | ||||||||||||||||||||||||||||||||||||
| number_of_consecutive_checks=6, | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| yield dsc_resource | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| for component_name, value in component_patch.items(): | ||||||||||||||||||||||||||||||||||||
| LOGGER.info(f"Waiting for component {component_name} to be updated.") | ||||||||||||||||||||||||||||||||||||
| if original_components[component_name]["managementState"] == DscComponents.ManagementState.MANAGED: | ||||||||||||||||||||||||||||||||||||
| dsc_resource.wait_for_condition( | ||||||||||||||||||||||||||||||||||||
| condition=DscComponents.COMPONENT_MAPPING[component_name], status="True" | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||
| component_name == DscComponents.MODELREGISTRY | ||||||||||||||||||||||||||||||||||||
| and value.get("managementState") == DscComponents.ManagementState.MANAGED | ||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||
| # Since namespace specified in registriesNamespace is automatically created after setting | ||||||||||||||||||||||||||||||||||||
| # managementStateto Managed. We need to explicitly delete it on clean up. | ||||||||||||||||||||||||||||||||||||
| namespace = Namespace(name=py_config["model_registry_namespace"], ensure_exists=True) | ||||||||||||||||||||||||||||||||||||
| if namespace: | ||||||||||||||||||||||||||||||||||||
| namespace.delete(wait=True) | ||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||
| LOGGER.info("Model Registry is enabled by default and does not require any setup.") | ||||||||||||||||||||||||||||||||||||
| yield dsc_resource | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| @pytest.fixture(scope="class") | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be move in the if I belive