Skip to content

Schema evolution and task cancellation

Hubuum v0.0.16 gives every class an immutable schema revision. A schema revision is independent of the class or object's resource revision. Select the typed service with client.classes.by_id(class_id).schema; AsyncClient exposes the same methods with await.

Stage, inspect, and activate

Changing the schema policy of a nonempty class through ordinary class PATCH now returns 409 Conflict. Stage the proposed policy, request an impact analysis, and explicitly activate it after inspecting the results:

from hubuum_client import (
    SchemaActivationPolicy,
    SchemaActivationRequest,
    SchemaImpactReadiness,
    SchemaStageRequest,
)

schema = client.classes.by_id(class_id).schema
active = schema.get().active
staged = schema.stage(
    SchemaStageRequest(
        json_schema={
            "type": "object",
            "required": ["hostname"],
            "properties": {"hostname": {"type": "string"}},
        },
        validate_schema=True,
    )
)
analysis = schema.impact(staged.revision)
client.tasks.wait(analysis.task_id, timeout_seconds=60)
report = schema.work(analysis.task_id)
if report.readiness != SchemaImpactReadiness.COMPATIBLE:
    raise RuntimeError("Inspect the impact findings and repair objects before activation")
activated = schema.activate(
    staged.revision,
    SchemaActivationRequest(
        expected_active_revision=active.revision,
        policy=SchemaActivationPolicy.REJECT_INCOMPATIBLE,
        impact_task_id=analysis.task_id,
    ),
)
if activated.task_id is not None:
    client.tasks.wait(activated.task_id, timeout_seconds=60)

Strict activation rechecks the active revision and object population inside the server transaction. Concurrent writes can invalidate a completed analysis and cause ConflictError; obtain a fresh impact analysis before retrying. SchemaActivationPolicy.ALLOW_PENDING requires an unscoped administrator and can activate without compatibility proof. Existing enforced objects then become pending until revalidated. schema.revalidate(revision) queues another scan; schema.abandon(revision) abandons a staged revision.

Class read permission allows revision reads. Class update permission allows staging, abandonment, and strict activation. Aggregate counts, impact and revalidation requests, work reports, and schema cancellation additionally require an unscoped administrator. See the server schema reference for the complete authorization and migration rules.

Saved findings and HTML reports

schema.work(task_id) returns typed counters, readiness, and impact details. impact.failures[*].samples contains all retained object IDs in each failure group for new analyses. impact.findings contains inspected object revisions, timestamps, and diagnostics. Legacy findings may have no snapshot. Diagnostics contain JSON Pointer locations, expected constraints, redacted actual type/size context, and omission/truncation indicators. expected.status distinguishes an available JSON null constraint from an omitted constraint.

These findings describe saved snapshots. Repairing an object does not update old diagnostics or HTML. Re-run impact after repairs to obtain current evidence.

from pathlib import Path
from hubuum_client import SchemaRepairReportRequest

html = schema.generate_report(
    analysis.task_id,
    SchemaRepairReportRequest(
        object_url_template="https://inventory.example/objects/{object_id}",
    ),
)
Path("schema-repair.html").write_text(html, encoding="utf-8")
retained_html = schema.report(analysis.task_id, download=True)

The object URL template must be an absolute HTTP(S) frontend URL with exactly one {object_id} placeholder and no credentials. Optional template_id selects a stored HTML layout. The report methods return HTML text. download=True requests attachment disposition; the caller chooses where to save the text.

Reports can exceed server assembly/output budgets and return APIError with status_code == 413. Failed generation preserves the previous HTML and saved findings. schema.cancel(task_id) cancels schema work while retaining committed findings; its report response can also exceed the assembly budget even when cancellation succeeded.

Compliance pages

Schema lists use numeric after continuations, distinct from ordinary opaque cursor queries. SchemaPageOptions is immutable and limits pages to 1–100 candidates. schema.revisions(options) returns one page; resume after its last revision. Compliance pages expose items and next_after, with no totals:

from hubuum_client import ComplianceStatus, SchemaPageOptions

options = SchemaPageOptions(limit=50)
while True:
    page = schema.objects(options, status=ComplianceStatus.PENDING)
    for item in page.items:
        print(item.object_id, item.status)
    if page.next_after is None:
        break
    options = SchemaPageOptions(after=page.next_after, limit=50)

An empty page can still have next_after: the continuation advances past inspected candidates that the caller cannot see. Evidence uses the wire field schema, exposed in Python as evidence.schema_.

Import activation

ImportClassInput.schema_activation accepts ImportSchemaActivation with revision, expected_active_revision, policy, and optional impact_task_id. The class input must contain the exact policy of an already staged revision. Import activation requires administrator authority and commits with that import item. Portable import envelopes remain version 2; full backups now use format 6. See server compatibility before upgrading or restoring older artifacts.

Cancel a task

from hubuum_client import TaskCancelRequest, TaskStatus

task = client.tasks.cancel(
    task_id,
    TaskCancelRequest(expected_status=TaskStatus.QUEUED, reason="Superseded import"),
)
if not task.status.terminal:
    task = client.tasks.wait(task_id, timeout_seconds=60)

Omit the payload to send an unconditional cancellation request. expected_status protects a queued-only withdrawal; a changed status returns ConflictError. Cancellation may return a running task while cleanup continues. It does not undo committed work. Inspect unattempted_items, terminal_reason, and remote_side_effect_state; possibly_sent means the remote system may already have been affected. Reasons are limited to 512 characters and omitted from model representations.

Task owners and unscoped administrators can cancel authorized tasks. Scoped tokens are restricted to tasks submitted with the same token, and internal reindex/schema work requires an unscoped administrator. Repeated requests for a terminal task return its existing terminal state.