Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
2ca7b04
feat(core): implement phase 1 and 2
AlessandroPomponio Feb 26, 2026
c96b761
feat(core): finish phase 2 implementation
AlessandroPomponio Feb 26, 2026
710b698
feat(core): implement phase 3
AlessandroPomponio Feb 26, 2026
38189cb
test: add tests
AlessandroPomponio Feb 26, 2026
2fefd72
feat(cli): add upgrade detection
AlessandroPomponio Feb 27, 2026
7cc9195
refactor(cli): update examples
AlessandroPomponio Feb 27, 2026
4ef7b90
refactor(cli): rename list-legacy to list-legacy-validators
AlessandroPomponio Feb 27, 2026
42a7727
refactor(cli): use set for field names
AlessandroPomponio Mar 6, 2026
669e42f
refactor(cli): remove useless code
AlessandroPomponio Mar 23, 2026
b1f8076
refactor: reduce duplication
AlessandroPomponio Mar 23, 2026
22aac8e
refactor(cli): do not hardcode field patterns
AlessandroPomponio Mar 23, 2026
7cf2266
refactor(cli): remove debug code
AlessandroPomponio Mar 24, 2026
0bbac99
fix(legacy): avoid modifying wrong fields in legacy validators
AlessandroPomponio Mar 24, 2026
1ed3874
feat(cli): add transaction safety to ado upgrade of legacy resources
AlessandroPomponio Mar 24, 2026
e7bc134
test: add tests for new functionalities
AlessandroPomponio Mar 24, 2026
68cd9df
refactor(core): simplify importing of legacy validators
AlessandroPomponio Mar 24, 2026
f5599d0
feat(core): add utility getters/setters for dictionaries
AlessandroPomponio Mar 24, 2026
3b81216
feat(core): add field_paths to validator metadata
AlessandroPomponio Mar 24, 2026
be8a4ce
refactor(legacy): simplify validators
AlessandroPomponio Mar 24, 2026
3551ab1
test: update tests
AlessandroPomponio Mar 24, 2026
a2d8707
feat(legacy): add support for validator dependencies
AlessandroPomponio Mar 24, 2026
8b9aa5f
feat(legacy): support suggesting validator dependencies
AlessandroPomponio Mar 24, 2026
193c1a6
feat(cli): support auto-handling validator dependencies
AlessandroPomponio Mar 24, 2026
c98c936
docs(website): add docs for legacy validators
AlessandroPomponio Mar 24, 2026
dc64909
test: fix tests
AlessandroPomponio Mar 24, 2026
bc22fb1
refactor(cli): use full paths for matching validators
AlessandroPomponio Mar 25, 2026
7cdc214
refactor(core): only support fully qualified field names in legacy va…
AlessandroPomponio Mar 25, 2026
419f411
refactor(core): rename field
AlessandroPomponio Mar 25, 2026
2709eb6
refactor(cli): remove unused function
AlessandroPomponio Mar 25, 2026
19fa9a9
refactor(cli): remove print_validator_suggestions
AlessandroPomponio Mar 25, 2026
189fee2
refactor(cli): consolidate duplicate functions into one
AlessandroPomponio Mar 25, 2026
1c44854
refactor: style improvements
AlessandroPomponio Mar 25, 2026
7be7396
refactor(cli): consolidate prints
AlessandroPomponio Mar 25, 2026
fbede11
refactor: remove useless parameter
AlessandroPomponio Mar 25, 2026
a4fc4c0
refactor(cli): reorder upgrade handler
AlessandroPomponio Mar 25, 2026
1aac034
refactor: improve readability
AlessandroPomponio Mar 25, 2026
e65336c
refactor: update function name
AlessandroPomponio Mar 25, 2026
5778cb4
test: avoid mocks
AlessandroPomponio Mar 25, 2026
dd28ea0
refactor(tests): use fixture for common metadata pattern
AlessandroPomponio Mar 25, 2026
c8cdb92
refactor(tests): remove tests that aren't needed anymore
AlessandroPomponio Mar 25, 2026
69cdbc8
refactor: remove changes that aren't needed anymore
AlessandroPomponio Mar 25, 2026
c168a99
refactor(test): avoid mocks
AlessandroPomponio Mar 25, 2026
4919a63
test: add isolated legacy validator registry fixtures
AlessandroPomponio Mar 25, 2026
2e81c54
fix(tests): update test
AlessandroPomponio Mar 25, 2026
44e2866
fix: use correct paths for deprecated sample store fields
AlessandroPomponio Mar 25, 2026
7d2e4d3
fix: restore migrate_module_name's original behaviour
AlessandroPomponio Mar 26, 2026
7072d18
fix: add deprecated fields to simulate field_validator behaviour
AlessandroPomponio Mar 26, 2026
29de01e
feat(legacy): add support for gt4sd migration
AlessandroPomponio Mar 26, 2026
f5d43b6
feat(legacy): add support for removing storageLocation
AlessandroPomponio Mar 26, 2026
9bf2219
refactor(cli): replace error print with info
AlessandroPomponio Mar 26, 2026
5a54b5e
test: force mysql on tests due to CI issues
AlessandroPomponio Mar 26, 2026
9a67199
Merge branch 'main' into ap_622_legacy_validator
AlessandroPomponio Mar 26, 2026
b05ae29
fix: use enum value
AlessandroPomponio Mar 26, 2026
d53db7b
fix: reload validators module to ensure we actually have everything l…
AlessandroPomponio Mar 26, 2026
2a669e5
fix: use session-scoped fixture to ensure state is consistent
AlessandroPomponio Mar 26, 2026
a305e86
Merge branch 'main' into ap_622_legacy_validator
AlessandroPomponio Mar 27, 2026
17e8084
docs(website): update ado upgrade documentation
AlessandroPomponio Mar 27, 2026
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
25 changes: 25 additions & 0 deletions orchestrator/cli/commands/upgrade.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,21 @@ def upgrade_resource(
click_type=HiddenPluralChoice(AdoUpgradeSupportedResourceTypes),
),
],
apply_legacy_validator: Annotated[
list[str] | None,
typer.Option(
"--apply-legacy-validator",
help="Apply legacy validators by identifier (e.g., 'samplestore_kind_entitysource_to_samplestore'). "
"Can be specified multiple times.",
),
] = None,
list_legacy_validators: Annotated[
bool,
typer.Option(
"--list-legacy-validators",
help="List available legacy validators for this resource type",
),
] = False,
) -> None:
"""
Upgrade resources and contexts.
Expand All @@ -52,12 +67,22 @@ def upgrade_resource(
# Upgrade all operations

ado upgrade operations

# List available legacy validators for sample stores

ado upgrade samplestores --list-legacy-validators

# Apply a legacy validator during upgrade

ado upgrade samplestores --apply-legacy-validator samplestore_kind_entitysource_to_samplestore
"""

ado_configuration: AdoConfiguration = ctx.obj

parameters = AdoUpgradeCommandParameters(
ado_configuration=ado_configuration,
apply_legacy_validator=apply_legacy_validator,
list_legacy_validators=list_legacy_validators,
)

method_mapping = {
Expand Down
2 changes: 2 additions & 0 deletions orchestrator/cli/models/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,5 @@ class AdoTemplateCommandParameters(pydantic.BaseModel):

class AdoUpgradeCommandParameters(pydantic.BaseModel):
ado_configuration: AdoConfiguration
apply_legacy_validator: list[str] | None = None
list_legacy_validators: bool = False
10 changes: 10 additions & 0 deletions orchestrator/cli/utils/legacy/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright IBM Corporation 2025, 2026
# SPDX-License-Identifier: MIT

"""Utilities for working with legacy validators"""

from orchestrator.cli.utils.legacy.list import list_legacy_validators

__all__ = ["list_legacy_validators"]

# Made with Bob
180 changes: 180 additions & 0 deletions orchestrator/cli/utils/legacy/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# Copyright IBM Corporation 2025, 2026
# SPDX-License-Identifier: MIT

"""Common utilities for legacy validator handling"""

from typing import TYPE_CHECKING

import pydantic

from orchestrator.cli.utils.output.prints import (
ERROR,
HINT,
INFO,
WARN,
console_print,
cyan,
)

if TYPE_CHECKING:
from orchestrator.core.legacy.metadata import LegacyValidatorMetadata
from orchestrator.core.resources import CoreResourceKinds


def extract_deprecated_field_paths(
error: pydantic.ValidationError | ValueError,
resource_type: "CoreResourceKinds | None" = None,
) -> tuple[set[str], dict[str, list[str]]]:
"""Extract field paths and error details from validation errors

This function handles both pydantic ValidationError and ValueError types.
For ValueError, it attempts to extract an underlying pydantic ValidationError
from the error's __cause__. If that fails, it falls back to simple string
matching on the error message using known field paths from the legacy
validator registry (requires resource_type parameter).

Args:
error: The validation error (pydantic.ValidationError or ValueError)
resource_type: The resource type to get field paths for (required for
ValueError fallback to string matching)

Returns:
Tuple of (full field paths, field error details mapping)
- full field paths: Set of full dotted paths like 'config.specification.module.moduleType'
- field error details: Maps full field path to list of error messages

Raises:
ValueError: Re-raises the original error if it is a ValueError without a
ValidationError cause and resource_type is not provided
"""
deprecated_field_paths: set[str] = set()
field_errors: dict[str, list[str]] = {}

# Handle pydantic ValidationError directly
if isinstance(error, pydantic.ValidationError):
for err in error.errors():
if err.get("loc"):
# Build the full dotted path from the location tuple
full_path = ".".join(str(loc) for loc in err["loc"])
deprecated_field_paths.add(full_path)

# Store the error message for this field path
if full_path not in field_errors:
field_errors[full_path] = []

# Build a descriptive error message
msg = err.get("msg", "")
if err.get("input"):
msg = f"{msg} (got: {err['input']})"

field_errors[full_path].append(msg)

return deprecated_field_paths, field_errors

# Handle ValueError - try to extract pydantic ValidationError from __cause__
if isinstance(error, ValueError):
if hasattr(error, "__cause__") and isinstance(
error.__cause__, pydantic.ValidationError
):
# Recursively handle the underlying ValidationError
return extract_deprecated_field_paths(error.__cause__, resource_type)

# Fallback to simple string matching on error message
if resource_type is None:
raise error

from orchestrator.core.legacy.registry import LegacyValidatorRegistry

error_msg = str(error)

# Get all field paths from registered validators for this resource type
validators = LegacyValidatorRegistry.get_validators_for_resource(resource_type)
known_deprecated_field_paths = {
path
for validator in validators
for path in validator.deprecated_field_paths
}

for field_path in known_deprecated_field_paths:
if field_path in error_msg:
deprecated_field_paths.add(field_path)
# For string matching fallback, we don't have detailed error messages
field_errors[field_path] = [
"Field validation failed (details in error message)"
]

return deprecated_field_paths, field_errors

# Should not reach here due to type hints, but handle gracefully
raise TypeError(f"Unsupported error type: {type(error)}")


def print_validator_suggestions_with_dependencies(
validators: list["LegacyValidatorMetadata"], resource_type: "CoreResourceKinds"
) -> None:
"""Print legacy validator suggestions with dependency information

This enhanced version resolves dependencies and shows validators in the
correct execution order, along with dependency information.

Args:
validators: List of applicable validators
resource_type: The resource type
"""
from orchestrator.core.legacy.registry import LegacyValidatorRegistry

# Get validator identifiers
validator_ids = [v.identifier for v in validators]
missing_deps = []

# Resolve dependencies to get correct order
try:
validator_ids, missing_deps = LegacyValidatorRegistry.resolve_dependencies(
validator_ids
)
except ValueError as e:
# Circular dependency detected
console_print(f"{ERROR}:{e}", stderr=True)

# Get ordered validators (filter out None values)
ordered_validators: list[LegacyValidatorMetadata] = []
for vid in validator_ids:
validator = LegacyValidatorRegistry.get_validator(vid)
if validator is not None:
ordered_validators.append(validator)

console_print(f"{INFO}The following validator(s) are a match:\n", stderr=True)
for i, validator in enumerate(ordered_validators, 1):
# Format and print validator info using the method
console_print(
validator.format_info(
index=i, show_dependencies=True, show_version_info=False
)
)
console_print()

# Warn about missing dependencies
if missing_deps:
console_print(
f"{WARN}Some dependencies are missing: {', '.join(missing_deps)}\n"
)

# Build command with all validators in correct order
validator_args = " ".join(
f"--apply-legacy-validator {v.identifier}" for v in ordered_validators
)
console_print(
f"{HINT}To attempt the upgrade using the suggested legacy validator(s) run:\n"
f"\t{cyan(f'ado upgrade {resource_type.value} {validator_args}')}\n"
)

# Show note about automatic dependency resolution
if len(ordered_validators) > len(validators):
console_print(
"[dim]Note: Additional validators were included to satisfy dependencies[/dim]\n"
)

console_print(
f"{HINT}To list all legacy validators run:\n"
f"\t{cyan(f'ado upgrade {resource_type.value} --list-legacy-validators')}"
)
45 changes: 45 additions & 0 deletions orchestrator/cli/utils/legacy/list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copyright IBM Corporation 2025, 2026
# SPDX-License-Identifier: MIT

"""Utilities for listing legacy validators"""

from orchestrator.cli.utils.output.prints import console_print
from orchestrator.core.legacy.registry import LegacyValidatorRegistry
from orchestrator.core.resources import CoreResourceKinds


def list_legacy_validators(resource_type: CoreResourceKinds) -> None:
"""List all available legacy validators for a specific resource type

Args:
resource_type: The resource type to list validators for
"""
# Import validators package to trigger registration via __init__.py
import orchestrator.core.legacy.validators # noqa: F401

# Get validators for this resource type
validators = LegacyValidatorRegistry.get_validators_for_resource(resource_type)

if not validators:
console_print(
f"\n[yellow]No legacy validators available for {resource_type.value}[/yellow]\n"
)
return

# Resources can be referenced by their CoreResourceKinds value or by shorthands
# from cli_shorthands_to_cli_names in orchestrator/cli/utils/resources/mappings.py
resource_cli_name = resource_type.value

console_print(f"Available legacy validators for {resource_cli_name}s:\n")

for i, validator in enumerate(validators, 1):
# Format and print validator info with version information using the method
console_print(
validator.format_info(
index=i, show_dependencies=True, show_version_info=False
)
)
console_print() # Add spacing between validators


# Made with Bob
Loading
Loading