feat(resource): add container infrastructure resource types
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 21s
CI / lint (pull_request) Successful in 27s
CI / helm (pull_request) Successful in 31s
CI / typecheck (pull_request) Successful in 1m9s
CI / quality (pull_request) Successful in 3m43s
CI / security (pull_request) Successful in 4m6s
CI / integration_tests (pull_request) Successful in 6m12s
CI / unit_tests (pull_request) Successful in 7m14s
CI / docker (pull_request) Successful in 2m7s
CI / coverage (pull_request) Successful in 11m52s
CI / e2e_tests (pull_request) Successful in 21m28s
CI / status-check (pull_request) Successful in 1s
CI / benchmark-regression (pull_request) Successful in 56m15s
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 21s
CI / lint (pull_request) Successful in 27s
CI / helm (pull_request) Successful in 31s
CI / typecheck (pull_request) Successful in 1m9s
CI / quality (pull_request) Successful in 3m43s
CI / security (pull_request) Successful in 4m6s
CI / integration_tests (pull_request) Successful in 6m12s
CI / unit_tests (pull_request) Successful in 7m14s
CI / docker (pull_request) Successful in 2m7s
CI / coverage (pull_request) Successful in 11m52s
CI / e2e_tests (pull_request) Successful in 21m28s
CI / status-check (pull_request) Successful in 1s
CI / benchmark-regression (pull_request) Successful in 56m15s
Add 7 container infrastructure resource types per ADR-039: container-runtime, container-image, container-mount, container-exec-env, container-port, container-volume, container-network. - YAML configs under examples/resource-types/ - Bootstrap registration via _resource_registry_container.py - Updated container-instance parent_types (container-runtime, container-image) and child_types (container-mount, container-exec-env, container-port) - container-runtime is top-level with auto-discovery rules (scan_depth: 1) - container-mount/exec-env/port inherit snapshot sandbox from instance - container-volume is user-addable with snapshot sandbox - container-network is read-only, no sandbox - Handler references are forward declarations (ADR-039) - 33 Behave BDD scenarios, 4 Robot integration tests - Updated BUILTIN_NAMES, service docstring, CHANGELOG ISSUES CLOSED: #831
This commit is contained in:
@@ -167,3 +167,4 @@ PIPE
|
||||
|
||||
# Git worktrees for parallel task branches
|
||||
worktrees/
|
||||
*.bak
|
||||
|
||||
@@ -475,3 +475,52 @@ Sandbox: `copy_on_write`.
|
||||
|
||||
A hard link (link count > 1). Parent: `fs-directory`. Leaf type.
|
||||
Sandbox: `copy_on_write`.
|
||||
|
||||
---
|
||||
|
||||
# Container Infrastructure Resource Types (#831)
|
||||
|
||||
## container-runtime
|
||||
|
||||
A container runtime engine (Docker, Podman, containerd, etc.).
|
||||
Top-level type with no parents. Children: `container-image`,
|
||||
`container-instance`, `container-volume`, `container-network`.
|
||||
Auto-discovers from Docker API endpoints and docker-compose files.
|
||||
User-addable. Sandbox: `none`.
|
||||
|
||||
## container-image
|
||||
|
||||
A container image identified by tag and/or digest.
|
||||
Parent: `container-runtime`. Child: `container-instance`.
|
||||
Auto-discovers from Dockerfiles and docker-compose files.
|
||||
User-addable. Sandbox: `none`.
|
||||
|
||||
## container-mount
|
||||
|
||||
A bind mount or volume mount inside a container instance.
|
||||
Parent: `container-instance`. Children: `fs-directory`, `fs-file`.
|
||||
Not user-addable (auto-discovered). Sandbox: `snapshot`.
|
||||
|
||||
## container-exec-env
|
||||
|
||||
Execution environment variables and configuration inside a container.
|
||||
Parent: `container-instance`. Leaf type.
|
||||
Not user-addable. Sandbox: `snapshot`.
|
||||
|
||||
## container-port
|
||||
|
||||
A port mapping between host and container (e.g. 8080:80/tcp).
|
||||
Parent: `container-instance`. Leaf type.
|
||||
Not user-addable. Sandbox: `snapshot`.
|
||||
|
||||
## container-volume
|
||||
|
||||
A named container volume managed by the runtime engine.
|
||||
Parent: `container-runtime`. Child: `fs-directory`.
|
||||
User-addable. Sandbox: `snapshot`.
|
||||
|
||||
## container-network
|
||||
|
||||
A container network (bridge, host, overlay, macvlan, or none).
|
||||
Parent: `container-runtime`. Leaf type.
|
||||
Not user-addable. Sandbox: `none`.
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# Spec reference: ADR-039, docs/specification.md ~lines 25096-25114
|
||||
schema_version: "1.0"
|
||||
name: container-exec-env
|
||||
description: "Execution environment variables and configuration inside a container"
|
||||
resource_kind: physical
|
||||
sandbox_strategy: snapshot
|
||||
user_addable: false
|
||||
built_in: true
|
||||
parent_types: ["container-instance"]
|
||||
child_types: []
|
||||
capabilities:
|
||||
read: true
|
||||
write: false
|
||||
sandbox: true
|
||||
checkpoint: true
|
||||
handler: "cleveragents.resource.handlers.container:ContainerChildHandler"
|
||||
@@ -0,0 +1,25 @@
|
||||
# Spec reference: ADR-039, docs/specification.md ~lines 25096-25114
|
||||
schema_version: "1.0"
|
||||
name: container-image
|
||||
description: "A container image identified by tag and/or digest"
|
||||
resource_kind: physical
|
||||
sandbox_strategy: none
|
||||
user_addable: true
|
||||
built_in: true
|
||||
cli_args:
|
||||
- name: image-ref
|
||||
type: string
|
||||
required: true
|
||||
description: "Image reference (e.g. ubuntu:22.04, ghcr.io/org/app:latest)"
|
||||
- name: digest
|
||||
type: string
|
||||
required: false
|
||||
description: "Image digest (sha256:...)"
|
||||
parent_types: ["container-runtime"]
|
||||
child_types: ["container-instance"]
|
||||
capabilities:
|
||||
read: true
|
||||
write: false
|
||||
sandbox: false
|
||||
checkpoint: false
|
||||
handler: "cleveragents.resource.handlers.container:ContainerImageHandler"
|
||||
@@ -0,0 +1,16 @@
|
||||
# Spec reference: ADR-039, docs/specification.md ~lines 25096-25114
|
||||
schema_version: "1.0"
|
||||
name: container-mount
|
||||
description: "A bind mount or volume mount inside a container instance"
|
||||
resource_kind: physical
|
||||
sandbox_strategy: snapshot
|
||||
user_addable: false
|
||||
built_in: true
|
||||
parent_types: ["container-instance"]
|
||||
child_types: ["fs-directory", "fs-file"]
|
||||
capabilities:
|
||||
read: true
|
||||
write: true
|
||||
sandbox: true
|
||||
checkpoint: true
|
||||
handler: "cleveragents.resource.handlers.container:ContainerChildHandler"
|
||||
@@ -0,0 +1,16 @@
|
||||
# Spec reference: ADR-039, docs/specification.md ~lines 25096-25114
|
||||
schema_version: "1.0"
|
||||
name: container-network
|
||||
description: "A container network (bridge, host, overlay, macvlan, or none)"
|
||||
resource_kind: physical
|
||||
sandbox_strategy: none
|
||||
user_addable: false
|
||||
built_in: true
|
||||
parent_types: ["container-runtime"]
|
||||
child_types: []
|
||||
capabilities:
|
||||
read: true
|
||||
write: false
|
||||
sandbox: false
|
||||
checkpoint: false
|
||||
handler: "cleveragents.resource.handlers.container:ContainerNetworkHandler"
|
||||
@@ -0,0 +1,16 @@
|
||||
# Spec reference: ADR-039, docs/specification.md ~lines 25096-25114
|
||||
schema_version: "1.0"
|
||||
name: container-port
|
||||
description: "A port mapping between host and container (e.g. 8080:80/tcp)"
|
||||
resource_kind: physical
|
||||
sandbox_strategy: snapshot
|
||||
user_addable: false
|
||||
built_in: true
|
||||
parent_types: ["container-instance"]
|
||||
child_types: []
|
||||
capabilities:
|
||||
read: true
|
||||
write: false
|
||||
sandbox: true
|
||||
checkpoint: true
|
||||
handler: "cleveragents.resource.handlers.container:ContainerChildHandler"
|
||||
@@ -0,0 +1,41 @@
|
||||
# Spec reference: ADR-039, docs/specification.md ~lines 25096-25114
|
||||
schema_version: "1.0"
|
||||
name: container-runtime
|
||||
description: "A container runtime engine (Docker, Podman, containerd, etc.)"
|
||||
resource_kind: physical
|
||||
sandbox_strategy: none
|
||||
user_addable: true
|
||||
built_in: true
|
||||
cli_args:
|
||||
- name: engine-type
|
||||
type: string
|
||||
required: true
|
||||
description: "Container engine type (docker, podman, containerd, lxc, lxd, cri-o, other)"
|
||||
- name: socket-path
|
||||
type: path
|
||||
required: false
|
||||
description: "Path to the container engine socket"
|
||||
- name: endpoint
|
||||
type: string
|
||||
required: false
|
||||
description: "Remote endpoint URL for the engine API"
|
||||
parent_types: []
|
||||
child_types: ["container-image", "container-instance", "container-volume", "container-network"]
|
||||
auto_discovery:
|
||||
enabled: true
|
||||
scan_depth: 1
|
||||
rules:
|
||||
- type: container-image
|
||||
pattern: "images/*"
|
||||
- type: container-instance
|
||||
pattern: "containers/*"
|
||||
- type: container-volume
|
||||
pattern: "volumes/*"
|
||||
- type: container-network
|
||||
pattern: "networks/*"
|
||||
capabilities:
|
||||
read: true
|
||||
write: false
|
||||
sandbox: false
|
||||
checkpoint: false
|
||||
handler: "cleveragents.resource.handlers.container:ContainerRuntimeHandler"
|
||||
@@ -0,0 +1,25 @@
|
||||
# Spec reference: ADR-039, docs/specification.md ~lines 25096-25114
|
||||
schema_version: "1.0"
|
||||
name: container-volume
|
||||
description: "A named container volume managed by the runtime engine"
|
||||
resource_kind: physical
|
||||
sandbox_strategy: snapshot
|
||||
user_addable: true
|
||||
built_in: true
|
||||
cli_args:
|
||||
- name: volume-name
|
||||
type: string
|
||||
required: true
|
||||
description: "Name of the volume"
|
||||
- name: driver
|
||||
type: string
|
||||
required: false
|
||||
description: "Volume driver (e.g. local, nfs)"
|
||||
parent_types: ["container-runtime"]
|
||||
child_types: ["fs-directory"]
|
||||
capabilities:
|
||||
read: true
|
||||
write: true
|
||||
sandbox: true
|
||||
checkpoint: true
|
||||
handler: "cleveragents.resource.handlers.container:ContainerVolumeHandler"
|
||||
@@ -0,0 +1,129 @@
|
||||
@container-infra
|
||||
Feature: Container infrastructure resource types
|
||||
Verifies that the 7 container infrastructure resource types
|
||||
(container-runtime, container-image, container-mount, container-exec-env,
|
||||
container-port, container-volume, container-network) are correctly
|
||||
defined, registered, and have proper parent/child relationships.
|
||||
|
||||
# ── YAML schema loading ─────────────────────────────────
|
||||
|
||||
Scenario Outline: Container infra YAML validates for container_infra
|
||||
Given the built-in "<type_name>" YAML for container_infra
|
||||
When I load the YAML via from_yaml_file for container_infra
|
||||
Then the container_infra schema should be valid
|
||||
And the container_infra schema name should be "<type_name>"
|
||||
And the container_infra schema resource_kind should be "physical"
|
||||
|
||||
Examples:
|
||||
| type_name |
|
||||
| container-runtime |
|
||||
| container-image |
|
||||
| container-mount |
|
||||
| container-exec-env |
|
||||
| container-port |
|
||||
| container-volume |
|
||||
| container-network |
|
||||
|
||||
# ── Domain model creation ───────────────────────────────
|
||||
|
||||
Scenario Outline: Container infra YAML loads as domain model for container_infra
|
||||
Given the built-in "<type_name>" YAML for container_infra
|
||||
When I load the YAML as a ResourceTypeSpec for container_infra
|
||||
Then the container_infra domain model should be valid
|
||||
And the container_infra domain model name should be "<type_name>"
|
||||
And the container_infra domain model resource_kind should be physical
|
||||
|
||||
Examples:
|
||||
| type_name |
|
||||
| container-runtime |
|
||||
| container-image |
|
||||
| container-mount |
|
||||
| container-exec-env |
|
||||
| container-port |
|
||||
| container-volume |
|
||||
| container-network |
|
||||
|
||||
# ── Parent/child relationships ──────────────────────────
|
||||
|
||||
Scenario: container-runtime is top-level with children for container_infra
|
||||
Given the built-in "container-runtime" YAML for container_infra
|
||||
When I load the YAML as a ResourceTypeSpec for container_infra
|
||||
Then the container_infra spec should have empty parent_types
|
||||
And the container_infra child_types should contain "container-image"
|
||||
And the container_infra child_types should contain "container-instance"
|
||||
And the container_infra child_types should contain "container-volume"
|
||||
And the container_infra child_types should contain "container-network"
|
||||
|
||||
Scenario: container-image parent is runtime for container_infra
|
||||
Given the built-in "container-image" YAML for container_infra
|
||||
When I load the YAML as a ResourceTypeSpec for container_infra
|
||||
Then the container_infra parent_types should contain "container-runtime"
|
||||
And the container_infra child_types should contain "container-instance"
|
||||
|
||||
Scenario: container-mount parent is instance for container_infra
|
||||
Given the built-in "container-mount" YAML for container_infra
|
||||
When I load the YAML as a ResourceTypeSpec for container_infra
|
||||
Then the container_infra parent_types should contain "container-instance"
|
||||
And the container_infra child_types should contain "fs-directory"
|
||||
And the container_infra child_types should contain "fs-file"
|
||||
|
||||
Scenario: container-volume parent is runtime for container_infra
|
||||
Given the built-in "container-volume" YAML for container_infra
|
||||
When I load the YAML as a ResourceTypeSpec for container_infra
|
||||
Then the container_infra parent_types should contain "container-runtime"
|
||||
And the container_infra child_types should contain "fs-directory"
|
||||
|
||||
# ── User-addable flags ──────────────────────────────────
|
||||
|
||||
Scenario Outline: Container infra user_addable flags for container_infra
|
||||
Given the built-in "<type_name>" YAML for container_infra
|
||||
When I load the YAML as a ResourceTypeSpec for container_infra
|
||||
Then the container_infra user_addable should be <addable>
|
||||
|
||||
Examples:
|
||||
| type_name | addable |
|
||||
| container-runtime | true |
|
||||
| container-image | true |
|
||||
| container-mount | false |
|
||||
| container-exec-env | false |
|
||||
| container-port | false |
|
||||
| container-volume | true |
|
||||
| container-network | false |
|
||||
|
||||
# ── BUILTIN_NAMES registration ──────────────────────────
|
||||
|
||||
Scenario Outline: Container infra types in BUILTIN_NAMES for container_infra
|
||||
Then BUILTIN_NAMES should contain "<type_name>" for container_infra
|
||||
|
||||
Examples:
|
||||
| type_name |
|
||||
| container-runtime |
|
||||
| container-image |
|
||||
| container-mount |
|
||||
| container-exec-env |
|
||||
| container-port |
|
||||
| container-volume |
|
||||
| container-network |
|
||||
|
||||
# ── Bootstrap registration ─────────────────────────────
|
||||
|
||||
Scenario: All 7 container infra types in BUILTIN_TYPES for container_infra
|
||||
Given the bootstrap builtin type definitions for container_infra
|
||||
Then BUILTIN_TYPES should contain "container-runtime" for container_infra
|
||||
And BUILTIN_TYPES should contain "container-image" for container_infra
|
||||
And BUILTIN_TYPES should contain "container-mount" for container_infra
|
||||
And BUILTIN_TYPES should contain "container-exec-env" for container_infra
|
||||
And BUILTIN_TYPES should contain "container-port" for container_infra
|
||||
And BUILTIN_TYPES should contain "container-volume" for container_infra
|
||||
And BUILTIN_TYPES should contain "container-network" for container_infra
|
||||
|
||||
# ── Coverage: verify CONTAINER_INFRA_TYPES module directly ──
|
||||
|
||||
Scenario: CONTAINER_INFRA_TYPES has exactly 7 entries for container_infra
|
||||
Then CONTAINER_INFRA_TYPES should have 7 entries for container_infra
|
||||
|
||||
Scenario: Each container infra type has handler reference for container_infra
|
||||
Then every CONTAINER_INFRA_TYPES entry should have a handler for container_infra
|
||||
|
||||
Scenario: container-runtime has auto-discovery rules for container_infra
|
||||
Then container-runtime in CONTAINER_INFRA_TYPES should have auto_discovery for container_infra
|
||||
@@ -0,0 +1,191 @@
|
||||
"""Step definitions for resource_type_container_infra.feature.
|
||||
|
||||
Tests container infrastructure resource types: container-runtime,
|
||||
container-image, container-mount, container-exec-env, container-port,
|
||||
container-volume, container-network.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import yaml
|
||||
from behave import given, then, when
|
||||
|
||||
from cleveragents.application.services._resource_registry_container import (
|
||||
CONTAINER_INFRA_TYPES,
|
||||
)
|
||||
from cleveragents.application.services._resource_registry_data import (
|
||||
BUILTIN_TYPES,
|
||||
)
|
||||
from cleveragents.domain.models.core.resource_type import (
|
||||
ResourceKind,
|
||||
ResourceTypeSpec,
|
||||
)
|
||||
from cleveragents.resource.schema import ResourceTypeConfigSchema
|
||||
|
||||
_EXAMPLES_DIR = (
|
||||
Path(__file__).resolve().parent.parent.parent / "examples" / "resource-types"
|
||||
)
|
||||
|
||||
|
||||
# ── Given steps ──────────────────────────────────────────
|
||||
|
||||
|
||||
@given('the built-in "{type_name}" YAML for container_infra')
|
||||
def step_builtin_yaml(context: object, type_name: str) -> None:
|
||||
path = _EXAMPLES_DIR / f"{type_name}.yaml"
|
||||
assert path.exists(), f"YAML not found: {path}"
|
||||
context.ci_yaml_path = path
|
||||
|
||||
|
||||
@given("the bootstrap builtin type definitions for container_infra")
|
||||
def step_bootstrap_defs(context: object) -> None:
|
||||
context.ci_builtin_defs = list(BUILTIN_TYPES)
|
||||
|
||||
|
||||
# ── When steps ───────────────────────────────────────────
|
||||
|
||||
|
||||
@when("I load the YAML via from_yaml_file for container_infra")
|
||||
def step_load_yaml(context: object) -> None:
|
||||
path: Path = context.ci_yaml_path
|
||||
schema = ResourceTypeConfigSchema.from_yaml_file(path)
|
||||
context.ci_schema = schema
|
||||
|
||||
|
||||
@when("I load the YAML as a ResourceTypeSpec for container_infra")
|
||||
def step_load_domain(context: object) -> None:
|
||||
path: Path = context.ci_yaml_path
|
||||
raw: dict[str, Any] = yaml.safe_load(path.read_text(encoding="utf-8"))
|
||||
spec = ResourceTypeSpec.from_config(raw)
|
||||
context.ci_spec = spec
|
||||
|
||||
|
||||
# ── Then steps: schema ───────────────────────────────────
|
||||
|
||||
|
||||
@then("the container_infra schema should be valid")
|
||||
def step_schema_valid(context: object) -> None:
|
||||
schema = context.ci_schema
|
||||
assert schema is not None
|
||||
assert isinstance(schema, ResourceTypeConfigSchema)
|
||||
|
||||
|
||||
@then('the container_infra schema name should be "{name}"')
|
||||
def step_schema_name(context: object, name: str) -> None:
|
||||
assert context.ci_schema.name == name
|
||||
|
||||
|
||||
@then('the container_infra schema resource_kind should be "{kind}"')
|
||||
def step_schema_kind(context: object, kind: str) -> None:
|
||||
assert context.ci_schema.resource_kind == kind
|
||||
|
||||
|
||||
# ── Then steps: domain model ─────────────────────────────
|
||||
|
||||
|
||||
@then("the container_infra domain model should be valid")
|
||||
def step_domain_valid(context: object) -> None:
|
||||
spec = context.ci_spec
|
||||
assert spec is not None
|
||||
assert isinstance(spec, ResourceTypeSpec)
|
||||
|
||||
|
||||
@then('the container_infra domain model name should be "{name}"')
|
||||
def step_domain_name(context: object, name: str) -> None:
|
||||
assert context.ci_spec.name == name
|
||||
|
||||
|
||||
@then("the container_infra domain model resource_kind should be physical")
|
||||
def step_domain_kind_physical(context: object) -> None:
|
||||
assert context.ci_spec.resource_kind == ResourceKind.PHYSICAL
|
||||
|
||||
|
||||
# ── Then steps: parent/child ─────────────────────────────
|
||||
|
||||
|
||||
@then("the container_infra spec should have empty parent_types")
|
||||
def step_empty_parents(context: object) -> None:
|
||||
spec = context.ci_spec
|
||||
assert spec.parent_types == [], f"Expected empty, got {spec.parent_types}"
|
||||
|
||||
|
||||
@then('the container_infra parent_types should contain "{name}"')
|
||||
def step_parent_contains(context: object, name: str) -> None:
|
||||
spec = context.ci_spec
|
||||
assert name in spec.parent_types, (
|
||||
f"'{name}' not in parent_types: {spec.parent_types}"
|
||||
)
|
||||
|
||||
|
||||
@then('the container_infra child_types should contain "{name}"')
|
||||
def step_child_contains(context: object, name: str) -> None:
|
||||
spec = context.ci_spec
|
||||
assert name in spec.child_types, f"'{name}' not in child_types: {spec.child_types}"
|
||||
|
||||
|
||||
# ── Then steps: user_addable ─────────────────────────────
|
||||
|
||||
|
||||
@then("the container_infra user_addable should be {addable}")
|
||||
def step_user_addable(context: object, addable: str) -> None:
|
||||
spec = context.ci_spec
|
||||
expected = addable.lower() == "true"
|
||||
assert spec.user_addable is expected, (
|
||||
f"Expected user_addable={expected}, got {spec.user_addable}"
|
||||
)
|
||||
|
||||
|
||||
# ── Then steps: BUILTIN_NAMES ────────────────────────────
|
||||
|
||||
|
||||
@then('BUILTIN_NAMES should contain "{name}" for container_infra')
|
||||
def step_builtin_names(context: object, name: str) -> None:
|
||||
assert name in ResourceTypeSpec.BUILTIN_NAMES, f"'{name}' not in BUILTIN_NAMES"
|
||||
|
||||
|
||||
# ── Then steps: BUILTIN_TYPES ────────────────────────────
|
||||
|
||||
|
||||
@then('BUILTIN_TYPES should contain "{name}" for container_infra')
|
||||
def step_builtin_types(context: object, name: str) -> None:
|
||||
defs: list[dict[str, Any]] = context.ci_builtin_defs
|
||||
names = [d["name"] for d in defs]
|
||||
assert name in names, f"'{name}' not in BUILTIN_TYPES: {names}"
|
||||
|
||||
|
||||
# ── Coverage: CONTAINER_INFRA_TYPES direct access ────────
|
||||
|
||||
|
||||
@then("CONTAINER_INFRA_TYPES should have 7 entries for container_infra")
|
||||
def step_infra_count(context: object) -> None:
|
||||
assert len(CONTAINER_INFRA_TYPES) == 7, (
|
||||
f"Expected 7, got {len(CONTAINER_INFRA_TYPES)}"
|
||||
)
|
||||
|
||||
|
||||
@then("every CONTAINER_INFRA_TYPES entry should have a handler for container_infra")
|
||||
def step_all_have_handler(context: object) -> None:
|
||||
for entry in CONTAINER_INFRA_TYPES:
|
||||
assert entry.get("handler") is not None, f"{entry['name']} has no handler"
|
||||
|
||||
|
||||
@then(
|
||||
"container-runtime in CONTAINER_INFRA_TYPES should have auto_discovery"
|
||||
" for container_infra"
|
||||
)
|
||||
def step_runtime_auto_discovery(context: object) -> None:
|
||||
runtime = next(d for d in CONTAINER_INFRA_TYPES if d["name"] == "container-runtime")
|
||||
ad = runtime.get("auto_discovery")
|
||||
assert ad is not None, "container-runtime missing auto_discovery"
|
||||
assert ad.get("enabled") is True
|
||||
assert ad.get("scan_depth") == 1
|
||||
rules = ad.get("rules", [])
|
||||
assert len(rules) >= 4, f"Expected >=4 rules, got {len(rules)}"
|
||||
# Verify Dockerfile/docker-compose patterns
|
||||
patterns = [r["pattern"] for r in rules]
|
||||
assert any("docker-compose" in p for p in patterns), (
|
||||
f"Missing docker-compose pattern: {patterns}"
|
||||
)
|
||||
@@ -0,0 +1,129 @@
|
||||
"""Helper script for container infrastructure resource type Robot tests.
|
||||
|
||||
Usage:
|
||||
python helper_resource_type_container_infra.py check-registration
|
||||
python helper_resource_type_container_infra.py check-instance-children
|
||||
python helper_resource_type_container_infra.py check-hierarchy
|
||||
python helper_resource_type_container_infra.py check-yaml-loading
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
_SRC = str(Path(__file__).resolve().parents[1] / "src")
|
||||
if _SRC not in sys.path:
|
||||
sys.path.insert(0, _SRC)
|
||||
|
||||
from cleveragents.application.services._resource_registry_data import ( # noqa: E402
|
||||
BUILTIN_TYPES,
|
||||
)
|
||||
from cleveragents.domain.models.core.resource_type import ( # noqa: E402
|
||||
ResourceTypeSpec,
|
||||
)
|
||||
from cleveragents.resource.schema import ( # noqa: E402
|
||||
ResourceTypeConfigSchema,
|
||||
)
|
||||
|
||||
_EXAMPLES = Path(__file__).resolve().parents[1] / "examples" / "resource-types"
|
||||
|
||||
_CONTAINER_INFRA_NAMES = [
|
||||
"container-runtime",
|
||||
"container-image",
|
||||
"container-mount",
|
||||
"container-exec-env",
|
||||
"container-port",
|
||||
"container-volume",
|
||||
"container-network",
|
||||
]
|
||||
|
||||
|
||||
def cmd_check_registration() -> None:
|
||||
"""Verify all 7 container infra types in BUILTIN_TYPES."""
|
||||
names = [d["name"] for d in BUILTIN_TYPES]
|
||||
for cn in _CONTAINER_INFRA_NAMES:
|
||||
assert cn in names, f"{cn} not in BUILTIN_TYPES: {names}"
|
||||
assert cn in ResourceTypeSpec.BUILTIN_NAMES, f"{cn} not in BUILTIN_NAMES"
|
||||
entry = next(d for d in BUILTIN_TYPES if d["name"] == cn)
|
||||
assert entry["resource_kind"] == "physical"
|
||||
assert entry["built_in"] is True
|
||||
print("check-registration-ok")
|
||||
|
||||
|
||||
def cmd_check_instance_children() -> None:
|
||||
"""Verify container-instance has updated children."""
|
||||
entry = next(d for d in BUILTIN_TYPES if d["name"] == "container-instance")
|
||||
for child in ("container-mount", "container-exec-env", "container-port"):
|
||||
assert child in entry["child_types"], (
|
||||
f"{child} not in container-instance children: {entry['child_types']}"
|
||||
)
|
||||
for parent in ("container-runtime", "container-image"):
|
||||
assert parent in entry["parent_types"], (
|
||||
f"{parent} not in container-instance parents: {entry['parent_types']}"
|
||||
)
|
||||
print("check-instance-children-ok")
|
||||
|
||||
|
||||
def cmd_check_hierarchy() -> None:
|
||||
"""Verify runtime -> image -> instance DAG."""
|
||||
by_name = {d["name"]: d for d in BUILTIN_TYPES}
|
||||
|
||||
runtime = by_name["container-runtime"]
|
||||
assert "container-image" in runtime["child_types"]
|
||||
assert "container-instance" in runtime["child_types"]
|
||||
assert "container-volume" in runtime["child_types"]
|
||||
assert "container-network" in runtime["child_types"]
|
||||
|
||||
image = by_name["container-image"]
|
||||
assert "container-runtime" in image["parent_types"]
|
||||
assert "container-instance" in image["child_types"]
|
||||
|
||||
mount = by_name["container-mount"]
|
||||
assert "container-instance" in mount["parent_types"]
|
||||
assert "fs-directory" in mount["child_types"]
|
||||
|
||||
volume = by_name["container-volume"]
|
||||
assert "container-runtime" in volume["parent_types"]
|
||||
assert "fs-directory" in volume["child_types"]
|
||||
|
||||
network = by_name["container-network"]
|
||||
assert "container-runtime" in network["parent_types"]
|
||||
assert network["child_types"] == []
|
||||
|
||||
print("check-hierarchy-ok")
|
||||
|
||||
|
||||
def cmd_check_yaml_loading() -> None:
|
||||
"""Load all 7 YAML configs and verify schema."""
|
||||
for cn in _CONTAINER_INFRA_NAMES:
|
||||
path = _EXAMPLES / f"{cn}.yaml"
|
||||
assert path.exists(), f"Missing YAML: {path}"
|
||||
schema = ResourceTypeConfigSchema.from_yaml_file(path)
|
||||
assert schema.name == cn
|
||||
assert schema.resource_kind == "physical"
|
||||
spec = ResourceTypeSpec.from_config(schema.model_dump())
|
||||
assert spec.name == cn
|
||||
print("check-yaml-loading-ok")
|
||||
|
||||
|
||||
_COMMANDS = {
|
||||
"check-registration": cmd_check_registration,
|
||||
"check-instance-children": cmd_check_instance_children,
|
||||
"check-hierarchy": cmd_check_hierarchy,
|
||||
"check-yaml-loading": cmd_check_yaml_loading,
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS:
|
||||
print(
|
||||
f"Usage: {sys.argv[0]} <{'|'.join(_COMMANDS)}>",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
_COMMANDS[sys.argv[1]]()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,47 @@
|
||||
*** Settings ***
|
||||
Documentation Integration tests for container infrastructure resource types.
|
||||
... Verifies all 7 container infra types are registered,
|
||||
... have correct parent/child relationships, and load from YAML.
|
||||
Resource ${CURDIR}/common.resource
|
||||
Suite Setup Setup Test Environment
|
||||
Suite Teardown Cleanup Test Environment
|
||||
|
||||
*** Variables ***
|
||||
${HELPER} ${CURDIR}/helper_resource_type_container_infra.py
|
||||
|
||||
*** Test Cases ***
|
||||
All Container Infra Types In BUILTIN_TYPES
|
||||
[Documentation] Verify all 7 container infra types in BUILTIN_TYPES
|
||||
[Tags] container registration smoke
|
||||
${result}= Run Process ${PYTHON} ${HELPER} check-registration
|
||||
... timeout=60s on_timeout=kill
|
||||
Should Be Equal As Integers ${result.rc} 0
|
||||
... Registration check failed: ${result.stderr}
|
||||
Should Contain ${result.stdout} check-registration-ok
|
||||
|
||||
Container Instance Updated With Children
|
||||
[Documentation] Verify container-instance has mount/exec-env/port children
|
||||
[Tags] container parent-child
|
||||
${result}= Run Process ${PYTHON} ${HELPER} check-instance-children
|
||||
... timeout=60s on_timeout=kill
|
||||
Should Be Equal As Integers ${result.rc} 0
|
||||
... Instance children check failed: ${result.stderr}
|
||||
Should Contain ${result.stdout} check-instance-children-ok
|
||||
|
||||
Container Runtime Hierarchy
|
||||
[Documentation] Verify runtime -> image -> instance DAG hierarchy
|
||||
[Tags] container hierarchy
|
||||
${result}= Run Process ${PYTHON} ${HELPER} check-hierarchy
|
||||
... timeout=60s on_timeout=kill
|
||||
Should Be Equal As Integers ${result.rc} 0
|
||||
... Hierarchy check failed: ${result.stderr}
|
||||
Should Contain ${result.stdout} check-hierarchy-ok
|
||||
|
||||
Container YAML Schema Loading
|
||||
[Documentation] Load all 7 container YAML configs and verify schema
|
||||
[Tags] container yaml schema
|
||||
${result}= Run Process ${PYTHON} ${HELPER} check-yaml-loading
|
||||
... timeout=60s on_timeout=kill
|
||||
Should Be Equal As Integers ${result.rc} 0
|
||||
... YAML loading failed: ${result.stderr}
|
||||
Should Contain ${result.stdout} check-yaml-loading-ok
|
||||
@@ -0,0 +1,272 @@
|
||||
"""Container infrastructure resource type definitions for the Resource Registry.
|
||||
|
||||
Contains the 7 built-in container infrastructure type entries
|
||||
(container-runtime, container-image, container-mount, container-exec-env,
|
||||
container-port, container-volume, container-network) that are appended
|
||||
to ``BUILTIN_TYPES`` in ``_resource_registry_data``.
|
||||
|
||||
Defined per ADR-039: Container Resource Types and the specification's
|
||||
built-in handler table (~line 25096).
|
||||
|
||||
.. note::
|
||||
|
||||
This is an **internal** module (``_``-prefixed). External code
|
||||
should import ``BUILTIN_TYPES`` from ``_resource_registry_data``
|
||||
instead.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
__all__ = ["CONTAINER_INFRA_TYPES"]
|
||||
|
||||
# Handler references per spec line 25096-25114. These handler classes
|
||||
# do not exist yet — the references are forward declarations that will
|
||||
# be resolved when the container runtime integration is implemented.
|
||||
_RUNTIME_HANDLER = "cleveragents.resource.handlers.container:ContainerRuntimeHandler"
|
||||
_IMAGE_HANDLER = "cleveragents.resource.handlers.container:ContainerImageHandler"
|
||||
_CHILD_HANDLER = "cleveragents.resource.handlers.container:ContainerChildHandler"
|
||||
_VOLUME_HANDLER = "cleveragents.resource.handlers.container:ContainerVolumeHandler"
|
||||
_NETWORK_HANDLER = "cleveragents.resource.handlers.container:ContainerNetworkHandler"
|
||||
|
||||
CONTAINER_INFRA_TYPES: list[dict[str, Any]] = [
|
||||
# ------------------------------------------------------------------
|
||||
# container-runtime: top-level runtime (Docker, Podman, etc.)
|
||||
# ------------------------------------------------------------------
|
||||
{
|
||||
"name": "container-runtime",
|
||||
"description": (
|
||||
"A container runtime engine (Docker, Podman, containerd, etc.)."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
"cli_args": [
|
||||
{
|
||||
"name": "engine-type",
|
||||
"type": "string",
|
||||
"required": True,
|
||||
"description": (
|
||||
"Container engine type "
|
||||
"(docker, podman, containerd, lxc, lxd, cri-o, other)."
|
||||
),
|
||||
},
|
||||
{
|
||||
"name": "socket-path",
|
||||
"type": "path",
|
||||
"required": False,
|
||||
"description": "Path to the container engine socket.",
|
||||
"default": None,
|
||||
},
|
||||
{
|
||||
"name": "endpoint",
|
||||
"type": "string",
|
||||
"required": False,
|
||||
"description": "Remote endpoint URL for the engine API.",
|
||||
"default": None,
|
||||
},
|
||||
],
|
||||
"parent_types": [],
|
||||
"child_types": [
|
||||
"container-image",
|
||||
"container-instance",
|
||||
"container-volume",
|
||||
"container-network",
|
||||
],
|
||||
"auto_discovery": {
|
||||
"enabled": True,
|
||||
"scan_depth": 1,
|
||||
"rules": [
|
||||
{"type": "container-image", "pattern": "images/*"},
|
||||
{"type": "container-instance", "pattern": "containers/*"},
|
||||
{"type": "container-volume", "pattern": "volumes/*"},
|
||||
{"type": "container-network", "pattern": "networks/*"},
|
||||
{"type": "container-image", "pattern": "docker-compose.yml"},
|
||||
{"type": "container-image", "pattern": "docker-compose.yaml"},
|
||||
{"type": "container-image", "pattern": "compose.yml"},
|
||||
{"type": "container-image", "pattern": "compose.yaml"},
|
||||
],
|
||||
},
|
||||
"handler": _RUNTIME_HANDLER,
|
||||
"capabilities": {
|
||||
"read": True,
|
||||
"write": False,
|
||||
"sandbox": False,
|
||||
"checkpoint": False,
|
||||
},
|
||||
},
|
||||
# ------------------------------------------------------------------
|
||||
# container-image: immutable container image (tag + digest)
|
||||
# ------------------------------------------------------------------
|
||||
{
|
||||
"name": "container-image",
|
||||
"description": ("A container image identified by tag and/or digest."),
|
||||
"resource_kind": "physical",
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
"cli_args": [
|
||||
{
|
||||
"name": "image-ref",
|
||||
"type": "string",
|
||||
"required": True,
|
||||
"description": (
|
||||
"Image reference (e.g. ubuntu:22.04, ghcr.io/org/app:latest)."
|
||||
),
|
||||
},
|
||||
{
|
||||
"name": "digest",
|
||||
"type": "string",
|
||||
"required": False,
|
||||
"description": "Image digest (sha256:...).",
|
||||
"default": None,
|
||||
},
|
||||
],
|
||||
"parent_types": ["container-runtime"],
|
||||
"child_types": ["container-instance"],
|
||||
"auto_discovery": {
|
||||
"enabled": True,
|
||||
"scan_depth": 1,
|
||||
"rules": [
|
||||
{"type": "container-image", "pattern": "Dockerfile"},
|
||||
{"type": "container-image", "pattern": "Dockerfile.*"},
|
||||
{"type": "container-image", "pattern": "*.dockerfile"},
|
||||
],
|
||||
},
|
||||
"handler": _IMAGE_HANDLER,
|
||||
"capabilities": {
|
||||
"read": True,
|
||||
"write": False,
|
||||
"sandbox": False,
|
||||
"checkpoint": False,
|
||||
},
|
||||
},
|
||||
# ------------------------------------------------------------------
|
||||
# container-mount: bind mount or volume mount inside a container
|
||||
# ------------------------------------------------------------------
|
||||
{
|
||||
"name": "container-mount",
|
||||
"description": ("A bind mount or volume mount inside a container instance."),
|
||||
"resource_kind": "physical",
|
||||
"sandbox_strategy": "snapshot",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
"cli_args": [],
|
||||
"parent_types": ["container-instance"],
|
||||
"child_types": ["fs-directory", "fs-file"],
|
||||
"handler": _CHILD_HANDLER,
|
||||
"capabilities": {
|
||||
"read": True,
|
||||
"write": True,
|
||||
"sandbox": True,
|
||||
"checkpoint": True,
|
||||
},
|
||||
},
|
||||
# ------------------------------------------------------------------
|
||||
# container-exec-env: execution environment inside a container
|
||||
# ------------------------------------------------------------------
|
||||
{
|
||||
"name": "container-exec-env",
|
||||
"description": (
|
||||
"Execution environment variables and configuration "
|
||||
"inside a container instance."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"sandbox_strategy": "snapshot",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
"cli_args": [],
|
||||
"parent_types": ["container-instance"],
|
||||
"child_types": [],
|
||||
"handler": _CHILD_HANDLER,
|
||||
"capabilities": {
|
||||
"read": True,
|
||||
"write": False,
|
||||
"sandbox": True,
|
||||
"checkpoint": True,
|
||||
},
|
||||
},
|
||||
# ------------------------------------------------------------------
|
||||
# container-port: port mapping on a container
|
||||
# ------------------------------------------------------------------
|
||||
{
|
||||
"name": "container-port",
|
||||
"description": (
|
||||
"A port mapping between host and container (e.g. 8080:80/tcp)."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"sandbox_strategy": "snapshot",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
"cli_args": [],
|
||||
"parent_types": ["container-instance"],
|
||||
"child_types": [],
|
||||
"handler": _CHILD_HANDLER,
|
||||
"capabilities": {
|
||||
"read": True,
|
||||
"write": False,
|
||||
"sandbox": True,
|
||||
"checkpoint": True,
|
||||
},
|
||||
},
|
||||
# ------------------------------------------------------------------
|
||||
# container-volume: named Docker/Podman volume
|
||||
# ------------------------------------------------------------------
|
||||
{
|
||||
"name": "container-volume",
|
||||
"description": ("A named container volume managed by the runtime engine."),
|
||||
"resource_kind": "physical",
|
||||
"sandbox_strategy": "snapshot",
|
||||
"user_addable": True,
|
||||
"built_in": True,
|
||||
"cli_args": [
|
||||
{
|
||||
"name": "volume-name",
|
||||
"type": "string",
|
||||
"required": True,
|
||||
"description": "Name of the volume.",
|
||||
},
|
||||
{
|
||||
"name": "driver",
|
||||
"type": "string",
|
||||
"required": False,
|
||||
"description": "Volume driver (e.g. local, nfs).",
|
||||
"default": None,
|
||||
},
|
||||
],
|
||||
"parent_types": ["container-runtime"],
|
||||
"child_types": ["fs-directory"],
|
||||
"handler": _VOLUME_HANDLER,
|
||||
"capabilities": {
|
||||
"read": True,
|
||||
"write": True,
|
||||
"sandbox": True,
|
||||
"checkpoint": True,
|
||||
},
|
||||
},
|
||||
# ------------------------------------------------------------------
|
||||
# container-network: container network configuration
|
||||
# ------------------------------------------------------------------
|
||||
{
|
||||
"name": "container-network",
|
||||
"description": (
|
||||
"A container network (bridge, host, overlay, macvlan, or none)."
|
||||
),
|
||||
"resource_kind": "physical",
|
||||
"sandbox_strategy": "none",
|
||||
"user_addable": False,
|
||||
"built_in": True,
|
||||
"cli_args": [],
|
||||
"parent_types": ["container-runtime"],
|
||||
"child_types": [],
|
||||
"handler": _NETWORK_HANDLER,
|
||||
"capabilities": {
|
||||
"read": True,
|
||||
"write": False,
|
||||
"sandbox": False,
|
||||
"checkpoint": False,
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -22,6 +22,9 @@ from cleveragents.application.services._resource_registry_cloud import (
|
||||
CLOUD_BASE_TYPES,
|
||||
GCP_AZURE_TYPES,
|
||||
)
|
||||
from cleveragents.application.services._resource_registry_container import (
|
||||
CONTAINER_INFRA_TYPES,
|
||||
)
|
||||
from cleveragents.application.services._resource_registry_lsp import (
|
||||
LSP_RESOURCE_TYPES,
|
||||
)
|
||||
@@ -177,14 +180,23 @@ _GIT_FS_CONTAINER_TYPES: list[dict[str, Any]] = [
|
||||
"default": None,
|
||||
},
|
||||
],
|
||||
"parent_types": ["git-checkout", "fs-directory"],
|
||||
"child_types": [],
|
||||
"parent_types": [
|
||||
"container-runtime",
|
||||
"container-image",
|
||||
"git-checkout",
|
||||
"fs-directory",
|
||||
],
|
||||
"child_types": [
|
||||
"container-mount",
|
||||
"container-exec-env",
|
||||
"container-port",
|
||||
],
|
||||
"handler": ("cleveragents.resource.handlers.devcontainer:DevcontainerHandler"),
|
||||
"capabilities": {
|
||||
"read": True,
|
||||
"write": True,
|
||||
"sandbox": True,
|
||||
"checkpoint": False,
|
||||
"checkpoint": True,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -263,6 +275,8 @@ BUILTIN_TYPES: list[dict[str, Any]] = [
|
||||
*CLOUD_BASE_TYPES,
|
||||
*AWS_TYPES,
|
||||
*GCP_AZURE_TYPES,
|
||||
# Container infrastructure types -- see _resource_registry_container.py (#831)
|
||||
*CONTAINER_INFRA_TYPES,
|
||||
*DATABASE_TYPE_DEFS,
|
||||
# Virtual core resource types -- see _resource_registry_virtual.py (#329)
|
||||
*VIRTUAL_CORE_TYPES,
|
||||
|
||||
@@ -16,6 +16,13 @@ development workflows. Built-in types are **unnamespaced** (no
|
||||
|-------------------|----------|------------------|--------------------------------|
|
||||
| ``git-checkout`` | physical | ``git_worktree`` | Git repository checkout |
|
||||
| ``fs-directory`` | physical | ``copy_on_write``| Filesystem directory |
|
||||
| ``container-runtime``| physical | ``none`` | Container runtime engine |
|
||||
| ``container-image``| physical | ``none`` | Container image (tag + digest) |
|
||||
| ``container-mount``| physical | ``snapshot`` | Bind/volume mount in container |
|
||||
| ``container-exec-env``| physical | ``snapshot`` | Container exec environment |
|
||||
| ``container-port`` | physical | ``snapshot`` | Container port mapping |
|
||||
| ``container-volume``| physical | ``snapshot`` | Named container volume |
|
||||
| ``container-network``| physical | ``none`` | Container network |
|
||||
| ``file`` | virtual | ``none`` | Cross-layer file identity |
|
||||
| ``directory`` | virtual | ``none`` | Cross-layer directory identity |
|
||||
| ``commit`` | virtual | ``none`` | Cross-repo commit identity |
|
||||
|
||||
@@ -24,7 +24,14 @@ BUILTIN_TYPE_NAMES: frozenset[str] = frozenset(
|
||||
"fs-directory",
|
||||
"fs-mount",
|
||||
"fs-file",
|
||||
"container-exec-env",
|
||||
"container-image",
|
||||
"container-instance",
|
||||
"container-mount",
|
||||
"container-network",
|
||||
"container-port",
|
||||
"container-runtime",
|
||||
"container-volume",
|
||||
"devcontainer-instance",
|
||||
"devcontainer-file",
|
||||
"postgres",
|
||||
|
||||
@@ -174,6 +174,8 @@ class ResourceTypeSpec(BaseModel):
|
||||
# Built-in resource type names (unnamespaced).
|
||||
# The actual frozenset lives in _resource_type_validation to keep
|
||||
# this module under the 500-line CONTRIBUTING limit.
|
||||
# Built-in resource type names — sourced from _resource_type_validation.py
|
||||
# to keep this module under the 500-line CONTRIBUTING limit.
|
||||
BUILTIN_NAMES: ClassVar[frozenset[str]] = BUILTIN_TYPE_NAMES
|
||||
|
||||
name: str = Field(
|
||||
|
||||
Reference in New Issue
Block a user