Validators Documentation

Overview

The validators layer provides structural validation of individual platform YAML files. A validator resolves the file’s kind, delegates loading to the matching service, and runs optional lifecycle hooks before and after validation.

Available validators:

Class

Module

Purpose

BaseValidator

validators.base_validator

Abstract base — error/message accumulation + hook hooks

PlatformValidator

validators.strata_validator

Validates a single platform YAML file by kind


BaseValidator

Abstract base class that all validators extend.

from abc import ABC, abstractmethod
from pathlib import Path

class BaseValidator(ABC):
    def __init__(self) -> None: ...

    def has_errors(self) -> bool: ...
    def has_messages(self) -> bool: ...
    def get_errors(self) -> List[str]: ...
    def get_messages(self) -> List[str]: ...

    @abstractmethod
    def validate(self, work_path: Path) -> bool: ...
    @abstractmethod
    def before_validate(self, work_path: Path) -> bool: ...
    @abstractmethod
    def after_validate(self, work_path: Path) -> bool: ...

Extending BaseValidator

from pathlib import Path
from strata.validators.base_validator import BaseValidator

class MyValidator(BaseValidator):
    def before_validate(self, work_path: Path) -> bool:
        # pre-checks, lifecycle hooks
        return True

    def validate(self, work_path: Path) -> bool:
        # main validation logic
        return True

    def after_validate(self, work_path: Path) -> bool:
        # post-validation cleanup, lifecycle hooks
        return True

PlatformValidator

Validates a single platform YAML file. The caller is responsible for calling the three phases in order.

from pathlib import Path
from strata.validators.strata_validator import PlatformValidator

file_path = Path("path/to/workspace.yaml")
work_path = Path("/workspace/root")

validator = PlatformValidator(file_path)

if not validator.before_validate(work_path):
    print("Pre-validation failed:", validator.get_errors())
elif not validator.validate(work_path):
    print("Validation failed:", validator.get_errors())
elif not validator.after_validate(work_path):
    print("Post-validation failed:", validator.get_errors())
else:
    print("Valid!", validator.detected_kind)

Constructor

PlatformValidator(
    file_path: Path,
    configuration_service=None,  # optional ConfigurationService for Phase 2 dynamic validation
)

Properties

Property

Type

Description

detected_kind

Optional[PlatformKind]

Resolved kind after before_validate() succeeds

service

Optional[BaseService]

Loaded service instance after validate() passes

Three-Phase Pipeline

before_validate(work_path)bool

  1. Checks the file exists — error if not found

  2. Parses YAML — error on malformed YAML

  3. Validates the document is a mapping — error otherwise

  4. Extracts and validates kind against PlatformKind enum — error for unknown kinds

  5. Optionally parses spec.lifecycle for lifecycle hook execution

  6. Executes the validate_before lifecycle phase (no-op when no hooks defined)

validate(work_path)bool

  1. Maps detected_kind to the appropriate service class

  2. Phase 1: Loads the file via service_class.load() — Pydantic structural validation

  3. Phase 2: If configuration_service was provided, runs service.validate(configuration_model, work_path) for cross-reference checks

after_validate(work_path)bool

Executes the validate_after lifecycle phase (no-op when no hooks defined).

Kind → Service Mapping

kind value

Service class

deployment

DeploymentService

environment

EnvironmentService

firewall

FirewallService

module

ModuleService

namespace

NamespaceService

platform_model

PlatformService

provider

ProviderService

resource

ResourceService

workspace

WorkspaceService

Note: configuration kind is intentionally excluded — ConfigurationService is a path-less singleton incompatible with BaseService.load(path).

With Configuration Service (Phase 2)

from strata.services.configuration_service import ConfigurationService
from strata.validators.strata_validator import PlatformValidator

config_svc = ConfigurationService.get_instance()
# ... load configuration ...

validator = PlatformValidator(
    file_path=Path("workspace.yaml"),
    configuration_service=config_svc,
)

validator.before_validate(work_path)
validator.validate(work_path)   # Phase 1 + Phase 2 cross-reference checks
validator.after_validate(work_path)

Error Accumulation

Errors accumulate in the validator across all three phases. They do not reset between phases.

validator = PlatformValidator(Path("bad.yaml"))
validator.before_validate(work_path)  # may add errors
validator.validate(work_path)         # may add more errors
print(validator.get_errors())         # all errors from all phases

Note: When Phase 1 Pydantic validation fails, the validator returns False but the error list may be empty for services (like WorkspaceService) that override get_validation_errors() to return dynamic validation errors only. Always check the return value of each phase, not just has_errors().