Skip to main content

ExecResult

Struct ExecResult 

Source
pub struct ExecResult {
Show 15 fields pub result_id: String, pub request_id: String, pub exec_id: Option<String>, pub parent_result_id: Option<String>, pub pc_id: String, pub exit_code: i32, pub skipped: Option<bool>, pub stdout: String, pub stderr: String, pub started_at: DateTime<Utc>, pub finished_at: DateTime<Utc>, pub stdout_object: Option<String>, pub stderr_object: Option<String>, pub manifest_id: Option<String>, pub collect_object: Option<String>,
}

Fields§

§result_id: String

v0.29 / Issue #19: agent-minted UUID, unique per (Command, PC) run. Replaces request_id as the projector’s primary key so broadcast Commands (commands.all / commands.group.X) — where N PCs share one request_id — finally persist all N results instead of silently dropping all but the first. Pre-v0.29 agents omit this field; it deserialises as the empty string, and Self::stable_result_id derives a deterministic UUIDv5 from (request_id, pc_id) so legacy payloads (a) get distinct ids across broadcast PCs (PC #2’s row stops being dropped) and (b) get the SAME id on JetStream redelivery (the new ON CONFLICT(result_id) DO NOTHING path correctly dedupes, so executions.success_count doesn’t double-count across retries).

§request_id: String

The NATS reply token. Still surfaced for joining back to the kanade run request/reply path. No longer unique across rows (broadcast Commands share it).

§exec_id: Option<String>

v0.29 / Issue #19: back-link to executions.exec_id. Copied from Command.exec_id by the agent. None for ad-hoc kanade run (no deployment) and for results emitted by pre-v0.29 agents (decoded via serde(default)).

§parent_result_id: Option<String>

#955: back-link to the parent run’s result_id for a finalize: hook’s own result row. Set by the agent to the triggering run’s result_id so the SPA can link the <job>__finalize row to (and from) the run whose collect it cleaned up. None for every ordinary run and every pre-#955 payload (serde(default) keeps older results decodable).

§pc_id: String§exit_code: i32§skipped: Option<bool>

Whether the agent ran the script, as the agent itself reports it.

  • Some(true): it published this result instead of running the script because policy (or this OS) said “not now”: deadline / revoke / version-pin / staleness / unsupported-OS. Such a result says nothing about the script’s outcome, so aggregations count it as skipped, never as a failure.
  • Some(false): authoritative “this is not a skip” — the script ran (whatever it exited, including 126 / 127, which a real script under sh returns for “not executable” / “command not found”), or the agent refused the command (EXIT_REJECTED_UNSIGNED). A refusal is deliberately not a skip: “this command was not authorised” is something the fleet’s failure counts must surface.
  • None: the key was absent, i.e. an agent that predates the flag. See Self::is_reported_skip and Self::skips_check_projection for how each consumer reads it.

Agents that know the flag always send it (never null), so absence means exactly “legacy agent”. The reserved exit codes only say why.

§stdout: String

stdout. Empty string when Self::stdout_object is set — the agent overflowed the bytes into crate::kv::OBJECT_RESULT_OUTPUT because the inline payload would have exceeded NATS’s default max_payload (#227). The backend projector derefs the pointer before inserting; SQLite still stores the full text inline so the SPA Activity page reads unchanged.

§stderr: String§started_at: DateTime<Utc>§finished_at: DateTime<Utc>§stdout_object: Option<String>

Object Store key under crate::kv::OBJECT_RESULT_OUTPUT when stdout overflowed the agent’s inline threshold (#227). Set to Some("<request_id>/<pc_id>/stdout") by the agent’s outbox drain; the backend projector fetches the bytes from that key and uses them in place of the (empty) stdout field. None for the common small-stdout case + every pre-#227 payload (serde(default) keeps older results decodable).

§stderr_object: Option<String>

Sibling of stdout_object for the stderr stream. Same key shape (<request_id>/stderr).

§manifest_id: Option<String>

v0.13: the manifest id that produced this result. Sourced from Command.id (which is the YAML manifest.id, e.g. "inventory-hw"). Distinct from the per-deploy UUID stored in Command.exec_id. The results projector uses this to look up the manifest’s inventory: hint and upsert inventory_facts rows for inventory-tagged jobs.

§collect_object: Option<String>

#219: Object Store key under crate::kv::OBJECT_COLLECTIONS for the bundle this run collected, when the job carried a collect: hint and the run succeeded. Set by the agent to Some("<pc_id>/<job_id>/<rfc3339>.zip") after it zips the script’s listed files and uploads the archive. None for every non-collect job + every pre-#219 payload (serde(default) keeps older results decodable). The SPA Collect page lists / downloads these straight from the bucket.

Implementations§

Source§

impl ExecResult

Source

pub fn stable_result_id(&self) -> String

Return the result_id if the agent supplied one (v0.29+ payloads always do), otherwise derive a stable UUIDv5 from (request_id, pc_id). The projector calls this before INSERT so legacy payloads still get a non-empty PK, AND so that JetStream redeliveries of the same legacy payload hash to the same id and dedupe via ON CONFLICT. Per-PC fan-out stays distinct (different pc_id → different hash).

Source

pub fn is_reported_skip(&self) -> bool

True when the agent reported this result as a skip. What every result tally (and execution_results.skipped) counts. A legacy result (None) is not one: tallies read those by exit code, exactly as they did before the flag existed.

Source

pub fn skips_check_projection(&self) -> bool

True when this result must be kept out of the check_status projection because the script never ran (#909). Follows the flag when the agent sent one. For a legacy result (None) it keeps the rule the projector applied before the flag existed: every reserved exit code 122..=127 is dropped. A legacy agent cannot tell its own skip from a script that really exited 126 / 127, so those real exits stay dropped for legacy agents too — unchanged behaviour, not a new guess. (The signature refusal is handled separately, see Self::is_signature_refusal.)

Source

pub fn is_signature_refusal(&self) -> bool

True for the agent’s signature refusal (#1165): exit EXIT_REJECTED_UNSIGNED under the refusal’s derived result_id. A script that merely exits 123 (xargs does, for one) carries an ordinary random id and is not a refusal.

Trait Implementations§

Source§

impl Clone for ExecResult

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ExecResult

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for ExecResult

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for ExecResult

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more