Files
RoleForge/app/schemas/domain.py
Sergey Antropoff 9727ff6402 Molecule и Docker-тесты: vendored create playbook и явная платформа образа
- Добавлен molecule docker create playbook (create.yml + tasks/create_network.yml)
  с правкой tmpfs: словарь из molecule-plugins приводится к списку строк для
  community.docker.docker_container; сценарии копируют playbook и задают
  provisioner.playbooks.create.
- Для systemd-платформ tmpfs задаётся списком строк вместо mounts.
- В опциях ОС — run_platform (каноническая архитектура после build); в
  TestHostSpec и hosts теста передаётся platform в molecule/docker_container,
  чтобы на ARM не падал /sbin/init из-за amd64 без --platform.
- Страницы роли (просмотр и создание): одна dashboard-карточка на всю ширину,
  вкладки Role details / Role file catalog в
2026-05-05 08:56:54 +03:00

167 lines
4.9 KiB
Python

from datetime import datetime
from typing import Any
from typing import Literal
from pydantic import BaseModel, Field, field_validator
class LintRoleFileError(BaseModel):
line: int | None = None
column: int | None = None
level: str = "error"
message: str = ""
class LintRoleFileRequest(BaseModel):
path: str = Field(min_length=1, max_length=255)
content: str = Field(default="", max_length=600_000)
class LintRoleFileResponse(BaseModel):
ok: bool
kind: Literal["yaml", "json"]
errors: list[LintRoleFileError] = Field(default_factory=list)
class RoleFilePayload(BaseModel):
path: str = Field(min_length=1, max_length=255)
content: str = ""
class RoleCreate(BaseModel):
name: str
source_type: str = Field(description="galaxy | git | inline")
source_ref: str = ""
category_id: str | None = None
description: str = ""
content: dict[str, Any] | None = None
files: list[RoleFilePayload] = Field(default_factory=list)
visibility: str = Field(default="personal", description="public | team | personal (new roles are always personal)")
team_id: str | None = Field(default=None, description="If set, role is created for this team (visibility=team).")
tags: list[str] = Field(default_factory=list, description="Labels for filtering and display (comma-separated in UI).")
os_families: list[str] = Field(
default_factory=list,
description="Target OS families: rhel, debian, alpine, suse, arch, bsd, windows, universal.",
)
class RoleUpdate(BaseModel):
name: str
source_type: str = Field(description="galaxy | git | inline")
source_ref: str = ""
category_id: str | None = None
description: str = ""
visibility: str = Field(default="personal", description="public | team | personal (new roles are always personal)")
team_id: str | None = Field(default=None, description="Required when setting visibility to team (or omit to keep current team).")
tags: list[str] = Field(default_factory=list)
os_families: list[str] = Field(default_factory=list)
class RoleYamlImportRequest(BaseModel):
name: str
tasks_yaml: str
source_ref: str = "imported.yml"
category_id: str | None = None
description: str = ""
files: list[RoleFilePayload] = Field(default_factory=list)
visibility: str = Field(default="personal", description="public | team | personal (new roles are always personal)")
tags: list[str] = Field(default_factory=list)
os_families: list[str] = Field(default_factory=list)
class RoleFilesReplaceRequest(BaseModel):
files: list[RoleFilePayload]
visibility: str = Field(default="personal", description="legacy; server assigns visibility on save")
class RoleForkRequest(BaseModel):
name: str = Field(min_length=1, max_length=120)
description: str = ""
source_type: str = Field(default="inline", description="galaxy | git | inline")
source_ref: str = ""
category_id: str | None = None
@field_validator("category_id", mode="before")
@classmethod
def _empty_category_to_none(cls, v: object) -> str | None:
if v is None or v == "":
return None
return str(v)
class RoleImportItem(BaseModel):
path: str = Field(min_length=1, max_length=255)
content: str = ""
class RoleImportResult(BaseModel):
suggested_name: str
items: list[RoleImportItem]
class RoleCategoryCreate(BaseModel):
name: str = Field(min_length=2, max_length=80)
class InventoryCreate(BaseModel):
name: str
inventory_text: str
class PlaybookCreate(BaseModel):
name: str
description: str = ""
playbook_yaml: str
inventory_id: str
role_ids: list[str] = Field(default_factory=list)
is_shared: bool = False
class PlaybookYamlImportRequest(BaseModel):
name: str
playbook_yaml: str
inventory_id: str
description: str = ""
role_ids: list[str] = Field(default_factory=list)
is_shared: bool = False
class AddRoleToPlaybookRequest(BaseModel):
role_id: str
class JobLaunchRequest(BaseModel):
playbook_id: str
extra_vars: dict[str, Any] = Field(default_factory=dict)
runtime_mode: Literal["docker", "k8s"] = "docker"
class JobResponse(BaseModel):
id: str
status: str
celery_task_id: str | None
created_at: datetime
class TestHostSpec(BaseModel):
name: str
image: str = "roleforge-backend:latest"
groups: list[str] = Field(default_factory=list)
command: str = "tail -f /dev/null"
privileged: bool = True
systemd: bool = False
# OCI platform for molecule/docker_container (e.g. linux/amd64 on ARM hosts for amd64-only images).
platform: str = ""
class TestLaunchRequest(BaseModel):
playbook_id: str | None = None
role_id: str | None = None
hosts: list[TestHostSpec] = Field(default_factory=list)
extra_vars: dict[str, Any] = Field(default_factory=dict)
runtime_mode: Literal["docker", "k8s"] = "docker"
class RunnerStopRequest(BaseModel):
runtime_mode: Literal["docker", "k8s"]