Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion dsl/extdeps/os/systemd.dag
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
module extdeps.os.systemd

import std.types { NonEmptyStr }
import std.types { NonEmptyStr, String }
import std.list { List }
import std.measure { ByteSize, byte_size, byte_size_count }
import extdeps.external_authority { ExternalAuthority }
import extdeps.uri { Uri, Https }
import extdeps.os.systemd_contracts { systemd_unit_active_state_wire_contract }
Expand Down Expand Up @@ -30,8 +31,32 @@ data systemd_memory_max_property: NonEmptyStr = "MemoryMax"

data systemd_memory_swap_max_property: NonEmptyStr = "MemorySwapMax"

data systemd_memory_high_property: NonEmptyStr = "MemoryHigh"

data systemd_cpu_weight_property: NonEmptyStr = "CPUWeight"

type SystemdCgroupMemoryLimit
= MemoryLimitUnbounded
| MemoryLimitBytes { bytes: ByteSize }
| MemoryLimitAbsent

fn parse_systemd_memory_limit_show(raw: String) -> SystemdCgroupMemoryLimit {
if raw == "" { MemoryLimitAbsent }
else { if raw == "infinity" { MemoryLimitUnbounded }
else { match parse_int(s: raw) {
Present { value: n } => MemoryLimitBytes { bytes: byte_size(count: n) }
Absent => MemoryLimitAbsent
} } }
}

fn systemd_memory_limit_wire_label(lim: SystemdCgroupMemoryLimit) -> String {
match lim {
MemoryLimitUnbounded => "infinity"
MemoryLimitBytes { bytes: b } => to_string(byte_size_count(b: b))
MemoryLimitAbsent => ""
}
}

fn parse_systemd_unit_active_state(raw: String) -> SystemdUnitActiveState? {
if raw == "active" { Present { value: Active } }
else { if raw == "inactive" { Present { value: Inactive } }
Expand Down
148 changes: 148 additions & 0 deletions dsl/gunbc/runner_unit_live_read.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
module gunbc.runner_unit_live_read

import std.types { NonEmptyStr, String, Bool }
import std.measure { ByteSize, byte_size, byte_size_count }
import std.disposition { Disposition, Terminal }
import extdeps.os.systemd {
SystemdCgroupMemoryLimit,
MemoryLimitUnbounded,
MemoryLimitBytes,
MemoryLimitAbsent,
parse_systemd_memory_limit_show,
systemd_memory_max_property,
systemd_memory_swap_max_property,
systemd_memory_high_property,
}
import gunbc.host_converge {
ConvergeTarget,
ConvergeVerdict,
Converged,
Drifted,
Absent,
converge_verdict,
}
import gunbc.ci_floor_measurement {
RunnerSliceCapEffectiveness,
CapEffective,
CapUnbounded,
}

type RunnerUnitLiveReadRow {
unit: NonEmptyStr
memory_max_raw: String
memory_swap_max_raw: String
memory_high_raw: String
probed_at: NonEmptyStr
}

type RunnerUnitMemoryLiveRead {
unit: NonEmptyStr
memory_max: SystemdCgroupMemoryLimit
memory_swap_max: SystemdCgroupMemoryLimit
memory_high: SystemdCgroupMemoryLimit
probed_at: NonEmptyStr
}

data gunbc_runner_unit_live_read_scope_disposition: Disposition = Terminal {
reason: "Slice-1 typed fixture/read carrier only: parses recorded systemctl-show wire values into SystemdCgroupMemoryLimit and projects onto ConvergeTarget PerSlotMemoryCap verdict semantics. Does NOT replace gunbc.runner_slot_show_effective_read ReadAbsent — the live fleet systemctl-show transport is a separate load-bearing node."
}

data gunbc_srv1_runner_unit_live_read_fixture: RunnerUnitLiveReadRow = RunnerUnitLiveReadRow {
unit: "actions-runner@srv1-01.service",
memory_max_raw: "infinity",
memory_swap_max_raw: "0",
memory_high_raw: "infinity",
probed_at: "2026-07-01",
}

fn runner_unit_live_read_typed(row: RunnerUnitLiveReadRow) -> RunnerUnitMemoryLiveRead {
RunnerUnitMemoryLiveRead {
unit: row.unit,
memory_max: parse_systemd_memory_limit_show(raw: row.memory_max_raw),
memory_swap_max: parse_systemd_memory_limit_show(raw: row.memory_swap_max_raw),
memory_high: parse_systemd_memory_limit_show(raw: row.memory_high_raw),
probed_at: row.probed_at,
}
}

fn memory_limit_to_cap_effectiveness(lim: SystemdCgroupMemoryLimit) -> RunnerSliceCapEffectiveness? {
match lim {
MemoryLimitUnbounded => Present { value: CapUnbounded }
MemoryLimitBytes { bytes: b } => Present { value: CapEffective { bytes: b } }
MemoryLimitAbsent => none
}
}

fn memory_limit_converge_verdict_for_bytes_value(
observed: SystemdCgroupMemoryLimit,
bytes_value: String,
) -> ConvergeVerdict {
match observed {
MemoryLimitAbsent => Absent
MemoryLimitUnbounded => Drifted
MemoryLimitBytes { bytes: b } =>
converge_verdict(
effective: to_string(byte_size_count(b: b)),
expected: bytes_value
)
}
}

fn memory_limit_converge_verdict(
observed: SystemdCgroupMemoryLimit,
desired: ByteSize,
) -> ConvergeVerdict {
memory_limit_converge_verdict_for_bytes_value(
observed: observed,
bytes_value: to_string(byte_size_count(b: desired))
)
}

fn per_slot_memory_limit_for_property(
read: RunnerUnitMemoryLiveRead,
property: String,
) -> SystemdCgroupMemoryLimit? {
if property == systemd_memory_max_property {
Present { value: read.memory_max }
} else { if property == systemd_memory_swap_max_property {
Present { value: read.memory_swap_max }
} else { if property == systemd_memory_high_property {
Present { value: read.memory_high }
} else { none } } }
}

fn converge_target_live_verdict(
read: RunnerUnitMemoryLiveRead,
target: ConvergeTarget,
) -> ConvergeVerdict? {
match target {
PerSlotMemoryCap { property: p, dropin_path: _, instance_glob: _, bytes_value: bv } =>
match per_slot_memory_limit_for_property(read: read, property: p) {
Present { value: observed } =>
Present { value: memory_limit_converge_verdict_for_bytes_value(observed: observed, bytes_value: bv) }
Absent => none
}
_ => none
}
}

fn runner_unit_memory_max_drifts_against_cap(
read: RunnerUnitMemoryLiveRead,
desired_cap: ByteSize,
) -> Bool {
match memory_limit_converge_verdict(observed: read.memory_max, desired: desired_cap) {
Drifted => true
_ => false
}
}

fn runner_unit_swap_max_converged(read: RunnerUnitMemoryLiveRead, target: ConvergeTarget) -> Bool {
match converge_target_live_verdict(read: read, target: target) {
Present { value: Converged } => true
_ => false
}
}

fn fixture_live_read() -> RunnerUnitMemoryLiveRead {
runner_unit_live_read_typed(row: gunbc_srv1_runner_unit_live_read_fixture)
}
22 changes: 22 additions & 0 deletions dsl/test/claim/os_systemd_witness_test.dag
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@ import extdeps.os.systemd {
Failed,
parse_systemd_unit_active_state,
systemd_unit_active_state_wire_label,
SystemdCgroupMemoryLimit,
MemoryLimitUnbounded,
MemoryLimitBytes,
MemoryLimitAbsent,
parse_systemd_memory_limit_show,
systemd_memory_limit_wire_label,
}
import std.measure { byte_size, byte_size_count }
import gunbc.ci_floor_measurement { gunbc_ci_runner_cgroup_memory_cap }
import std.types { NonEmptyStr }

test fn witness_active_wire_decodes_to_active() -> Bool {
Expand All @@ -28,3 +36,17 @@ test fn witness_wrong_case_does_not_decode() -> Bool {
test fn witness_roundtrip_failed() -> Bool {
systemd_unit_active_state_wire_label(s: Failed) == "failed"
}

test fn witness_memory_limit_infinity_roundtrip() -> Bool {
systemd_memory_limit_wire_label(lim: MemoryLimitUnbounded) == "infinity"
}

test fn witness_memory_limit_bytes_roundtrip() -> Bool {
let cap = gunbc_ci_runner_cgroup_memory_cap
let raw = to_string(byte_size_count(b: cap))
match parse_systemd_memory_limit_show(raw: raw) {
MemoryLimitBytes { bytes: b } =>
systemd_memory_limit_wire_label(lim: MemoryLimitBytes { bytes: b }) == raw
_ => false
}
}
Loading
Loading