Repository navigation
fix(factory_reset): adapt result parsing to omnect-os-init JSON contract - #207
JanZachmann merged 21 commits into
Conversation
|
FYI @mlilien |
## Summary Adds the factory-reset wipe modes 2 and 3. Mode 2 overwrites `etc` and `data` with data from `/dev/urandom`, mode 3 discards every block of both with the `BLKDISCARD` ioctl. The wipe runs between the backup and the reformat and never aborts the reset: a failure on one partition does not skip the other, reformat and restore still run, and the outcome is reported as `Error` with the reason in `error`. Mode 4 is no longer an accepted mode. It also closes what the on-device tests found: a trigger the init cannot parse is answered instead of ignored, and a preserve configuration that says nothing is rejected instead of skipped. A blank trigger is not one of those — it is no reset request at all. Design: `docs/superpowers/specs/2026-08-11-factory-reset-modes-2-3-4-design.md`, with a correction of what it claimed about rejected triggers. ## Reason The legacy shell init implemented the wipe with external tools. Both are replaced by native code, so the behaviour differs in ways worth stating. **Mode 2** covers the whole partition from byte 0. The legacy `dd` started at offset 2048 so an interrupted wipe kept the volume label readable; the init finds partitions by number, never by label, and re-creates the filesystem anyway. Privacy is the same or better. The legacy `dd` also used `oflag=direct`; the write loop goes through the page cache instead and flushes every gigabyte and again at the end, so after a power loss at most one gigabyte of already overwritten blocks can still hold the old content. **Mode 3** issues one `BLKDISCARD` over the whole partition. The legacy path mounted it, removed the files and ran `fstrim`, which trims only the blocks the remove had freed and leaves journal remnants — and counted a failed mount as a successful wipe. Without discard support the ioctl now fails cleanly and the reset reports it. Discard stays a hint on some disks, so the "no total privacy guarantee" note in the layer README remains true. Both modes open the device with `O_EXCL`, so a partition that is still mounted fails with `EBUSY` rather than being overwritten under a live filesystem. **Mode 4 is dropped, not ported.** It handed the wipe — the one step the caller asked for — to a customer-supplied hook that is not built or tested here, and modes 2 and 3 cover rotating disks and flash. The removal has to land in the same release wherever the mode is known: the mode table in the meta layer README, and the mode type in omnect/omnect-device-service#207 and omnect/omnect-ui#128. A failed wipe reports `Error`, not `Warning`: the device is usable, but the operation the caller asked for did not happen. For modes 2 and 3 the destructive boundary moves to the wipe, so every outcome from there on reports `data_wiped` — a partly written overwrite destroys the data even when the reformat never runs. **A trigger the init cannot use is now answered.** It used to produce a kmsg warning only: the boot continued as Normal, no `factory_reset` object was written, and the trigger kept its value, so the warning repeated on every boot while the caller waited for a result. Such a trigger is now cleared like any other and reported with the status the shell implementation used — anything about `mode` is `Invalid`, anything about the preserve configuration is `ConfigError`. That also makes `preserve` mandatory again, and a file in `factory-reset.d` without a usable `paths` array an error rather than something to skip, since skipping it wipes the paths the caller asked to keep and still reports success. **A blank trigger is not a reset request.** GRUB reports a key set to an empty value as present where U-Boot reports it as unset, and the shell init cleared the key by writing an empty string. A device coming from it would otherwise be answered with one "reset failed" nobody asked for. The statuses a configuration problem reports are now the same wherever it occurs: a `factory-reset.json` that does not exist reports `ConfigError` like a key missing inside it, which is what the shell implementation did too. Only an absent file counts — a path that exists but cannot be read stays an I/O error and reports `Error`, because the request was usable and the storage was not, which is worth retrying. The `error` field itself has one shape everywhere now: it sits inside the factory-reset result, so no writer repeats the outer `Factory reset error:` prefix. **Known gap.** Nothing pins that the wipe runs after the backup and after the unmount — moving `wipe_and_rebuild` above `backup_all` passes the whole suite. The `wipe_and_rebuild` seam starts after both steps, so covering the order against them needs a mount/backup seam as well. Tracked separately, together with the `data_dev` / `etc_dev` argument swap, which has the same shape. The result schema is unchanged and the initramfs image needs no new packages: the wipe uses one ioctl and `/dev/urandom` instead of `fstrim`, `blkdiscard` or `dd`. --------- Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
omnect-os-init writes the factory-reset result with key "factory_reset", status codes 0..=4 and optional error/context/paths fields. The old parser expected key "factory-reset" and required error/paths, so the result was never reported. - read key "factory_reset" - add status 4 (warning: reset ok, but a partition needed a second mkfs) - error, context and paths are optional with defaults - a future unknown status code parses as Unknown instead of failing ODS startup; the raw result is logged - status is u32 to match the omnect-os-init serialization - bump feature version to 4 because the reported result shape changed Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
Twin reports are merge patches: a key omitted from a new result keeps the value of a previous one. Report error and context as null instead of omitting them, so a successful reset clears the error of an earlier failed one. - report data_wiped: on error it distinguishes a safe abort from a failure after data was already wiped - rename status variants to the omnect-os-init names (wire format unchanged; the old names described the codes wrongly in logs) - align warning testfile with real omnect-os-init output (warning carries a context note, no error) - test explicit "factory_reset": null and that FactoryReset::new() succeeds with an unknown status code - document the result fields in the README Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
Mode 4 ran a customer-supplied wipe hook in the initramfs. The hook is gone and the initramfs no longer accepts the mode: a trigger carrying it fails to parse, the device boots normally and no result is reported at all, so a request sent from the cloud or the local UI would never be answered. Rejecting the value here keeps that request from being sent in the first place. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
5c778a4 to
cb539aa
Compare
Version set to 0.46.0, above the released 0.45.5, because this branch changes the factory-reset result contract. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
The last upstream merge took the lock entry of upstream main. With a lock file that does not match Cargo.toml, a --frozen build fails. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
The initramfs docs moved from meta-omnect to omnect-os-init docs/usage.md. The README also called `preserve` optional, but a payload without it is rejected. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
…ry-reset-json-contract Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com> # Conflicts: # Cargo.lock # Cargo.toml
Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
A result without data_wiped is now unusable, so it no longer reads as a safe abort. Log a warning for the legacy factory-reset key, test unknown, negative and out-of-range status codes, test that mode 4 and a missing preserve list are rejected, and align the README and test files with the init output. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
The files in testfiles/positive/contract are copies of the factory reset results that omnect-os-init writes for every outcome. omnect-os-init checks that it writes exactly these files, and this test checks that ods parses each of them. A change on one side then fails a test when the copies are updated. Signed-off-by: Jan Zachmann 50990105+JanZachmann@users.noreply.github.com
…config Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
The error-after-wipe and warning files now hold values the init writes: a Warning has no error, and a failed restore is an Error. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
…ate validation config (#40) ## Summary Keeps behaviours of the scripted initramfs that the init did not keep, and removes a stale update validation config: - `/run` is mounted with `nosuid`, `nodev`, `strictatime` and mode 0755. - The links in `/etc/omnect/create-fs-links.txt` (one `<target> <link>` pair per line) are created once, then the file is deleted. - With `persistent-var-log`, `/var/log` is `/mnt/data/log`, the directory of the scripted initramfs. The fsck logs are written to `/mnt/data/log/fsck`. - After `switch_root` the initramfs files are deleted, so their RAM is freed. The delete does not cross into mounted filesystems, and the number of deleted entries is logged. When `/` is not a ramfs or tmpfs, the files are kept and the reason is logged. - A factory reset restore creates missing parent directories with the owner and mode from the backup. - The fsck logs and the flash mode logs are readable only by root: files 0600, the fsck log directory 0700. The scripted initramfs kept fsck output only in the status file, which has mode 0600. - `parted` runs in script mode, and a failed `parted print` is an error. - An empty `bootpart_fsuuid=` fails at once with a clear error instead of waiting for the timeout. - An fsck record in the env that does not unpack or has no exit code line (the format of the scripted initramfs) is deleted instead of staying there on every later boot. The text of a record without exit code is logged first. - On a boot without `omnect_validate_update`, `/mnt/data/var/lib/omnect-device-service/update_validation_conf.json` is deleted. A missing file is not an error, a failed delete is logged as a warning. On a validation boot the file is kept. - `tests/contract` holds the status file for every factory reset result, and a test checks that the init writes exactly these files. - `docs/usage.md` describes the symlinks, the removal of the update validation config, and that a failed EFI boot entry ends a flash mode with a fatal error. - `docs/usage.md` and the README warn that flash mode 3 does not check TLS certificates on a machine without `rtc`. - Two wrong comments are fixed: the init does not create `/mnt/cert/ca` and `/mnt/cert/priv`, and GRUB probes the boot partition of its own disk. ## Reason These are behaviours of the scripted initramfs that the init did not keep, or kept only partly. A device updated from the scripted initramfs would lose its log history and its one-time links, and run with a `/run` that allows suid binaries and device files. omnect/meta-omnect#697 builds the init into omnect-os and needs these fixes. omnect-device-service writes the update validation config before the reboot into the validation boot of an update it runs itself (`set_boot_partition`), and removes it after a successful validation. A failed validation reboots without removing it. An ADU update does not go through omnect-device-service: the swupdate handler only sets `omnect_validate_update_part` and does not write the file, but the validation reads it. So after a failed local validation, the next ADU update is validated as a local one: no IoT Hub connection check, and deviceupdate-agent is not started by omnect-device-service. A config written by hand is also deleted on every boot without validation; omnect/omnect-device-service#207 adds this to the update validation docs. The contract files close a gap that no device test can see: the status file is the interface to omnect-device-service. omnect/omnect-device-service#207 adds a copy of these files and a test that parses each one. The error-after-wipe and warning cases are built by the factory reset status code, so they hold values the init really writes. --------- Signed-off-by: Jan Zachmann 50990105+JanZachmann@users.noreply.github.com
mlilien
left a comment
There was a problem hiding this comment.
Review notes; inline comments below. Two points that cannot be anchored inline:
src/web_service.rs(FactoryResetV1): the publishedFactoryResetResultpayload changes incompatibly (errorcan benull, newdata_wiped, status4/4294967295), but the publish channel staysFactoryResetV1, and/factory-reset/v1now rejects mode 4. An omnect-ui build without omnect/omnect-ui#132 can't detect the change and breaks onerror: null/status: 4. The twin feature version was bumped to 4; should the local channel/endpoint get a V2 as well?- Commit
827a8c2: theSigned-off-bytrailer has no angle brackets around the email (Signed-off-by: Jan Zachmann 50990105+...@users.noreply.github.com), so a DCO check will treat it as unsigned.
An unknown status code is reported as it is, so the cloud can tell one future code from another. A non-null result under the legacy key 'factory-reset' is reported as unusable instead of as no reset, and a null legacy key is a normal boot without a warning. The README notes that a result from before feature version 4 can stay in the twin. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
…and configuration Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
|
@mlilien on the two points in the review summary:
|
mlilien
left a comment
There was a problem hiding this comment.
The local web service still publishes on FactoryResetV1 ("FactoryResetResult") and serves /factory-reset/v1, but the payload changed: error can be null, data_wiped is new, status 4 now means Warning, and mode 4 is rejected. Only the twin feature version was bumped. An omnect-ui without #132 gets no signal that the format changed. If the joint release is the only safeguard, could the description say so explicitly? Otherwise, should the channel or route get a new version?
ods checks preserve against the keys of factory-reset.json on the device and applications, not against the omnect-os-init topic list. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
|
@mlilien on the review summary: the description now says it explicitly. The channel |
…usable A top level of [], a string or null read as no reset, so the twin kept the previous result. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
JoergZeidler
left a comment
There was a problem hiding this comment.
Review of the factory-reset result contract change. Findings below reference file:line; none land on a line this diff touches (web_service.rs is unchanged and connect_web_service is outside the diff), so they are in the body rather than inline.
Contract verified against omnect-os-init at 9083856 (#40): key factory_reset, status codes 0..=4 as u32, field shapes (error/context optional, paths default, data_wiped required), and the six testfiles/positive/contract/ files are byte-for-byte copies of its tests/contract/. README claims about paths, the mandatory /etc/omnect/factory-reset.d/, and data_wiped match the producer. Mode 4 removal is consistent.
Machine checks on the PR head: cargo fmt -- --check passes; cargo audit passes (only pre-existing allowed warnings). clippy and the test suite could not run locally — azure-iot-sdk-sys needs the azure-iot-sdk-dev pkg-config library, absent here; they run in Concourse.
Medium (decision) — the web-service factory-reset surface changed its contract, but the README "Local web service" section was not updated.
The published channel FactoryResetResult (PublishChannel::FactoryResetV1, src/web_service.rs:58,70) now carries the new result shape, and the route /factory-reset/v1 (src/web_service.rs:184) now rejects mode 4. The README diff updated only the twin "Report factory reset result" section; the "Local web service" section is unchanged. The v1/V1 label stays on an incompatible payload, so an omnect-ui without omnect/omnect-ui#132 gets no signal and the only safeguard is a coordinated release (as the PR body notes).
- Decision: accept the coordinated release as the safeguard, or move the channel and route to
v2so an old consumer fails loudly instead of misreadingv1? - At minimum, update the README "Local web service" section for the new payload and the rejected mode 4.
Low (decision) — web service drops the last reset result on the next boot; the twin keeps it.
src/twin/factory_reset.rs:149 (connect_web_service) publishes the full self.report, so on a boot with no reset it sends {keys} with no result and the consumer sees nothing. connect_twin instead omits the key, so the twin's merge patch keeps the last result (as newly documented). After a reset and a later unrelated reboot, the twin still shows the result but omnect-ui no longer does. The publish logic is pre-existing, but the PR documents the twin-keeps behaviour without noting the web service differs — please confirm it is intended and consider a one-line note in the "Local web service" section.
Low — new tests use unwrap() where the convention prefers expect("reason").
e.g. src/twin/factory_reset.rs:408,416,421 and the loops in factory_reset_status_test. The new new_factory_reset helper already uses expect(...), and sibling tests use unwrap() throughout, so this only matches the existing style — raise only if the team wants to tighten test style here.
Minor — test coverage gap. No test exercises connect_web_service for the unusable result, which is exactly where the web-service/twin asymmetry (Low finding above) lives. Otherwise the parser error paths, no-reset/legacy cases, unknown-status round-trip, twin_report null-vs-omit, and the mode-4/missing-preserve rejections are all covered.
The published factory reset status has the twin result format, and has no result on a later boot while the twin keeps the last one. The factory reset route checks mode and preserve as the direct method does. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
|
@JoergZeidler on your review:
Your review is set to "changes requested" with an empty body. If anything beyond these points is open, please say which. |
JoergZeidler
left a comment
There was a problem hiding this comment.
Round-2 review after the feedback commits. All substantive findings from the first review are resolved: the README "Local web service" section now documents the published payload and the web-service/twin result asymmetry, and the tests use expect(...) instead of unwrap(). Contract still matches omnect-os-init #40. cargo fmt -- --check passes; clippy and tests run in Concourse. Only an optional minor test-coverage item remains (connect_web_service with an unusable result).
…eset mode 4 (#132) Closes #125 ## Summary omnect-os moves from the scripted initramfs to [omnect-os-init](https://github.com/omnect/omnect-os-init), an init written in Rust (omnect/meta-omnect#697). The new init writes a different factory-reset result and does not support reset mode 4; omnect/omnect-device-service#207 publishes this result as `factory_reset` feature version 4. This PR makes omnect-ui follow both changes. It also changes the probe of wifi-commissioning-service. ### Factory reset result of feature version 4 - The factory-reset result types follow the format omnect-device-service publishes with feature version 4: `status` is a `u32` with the codes `success`, `invalid`, `error`, `configError` and `warning` plus a catch-all, `error` and `context` are `null` when nothing went wrong, and the new `data_wiped` flag tells whether the reset had already started to change data. The required omnect-device-service version is `>=0.46.1`. - The domain status enum has two variants that are not a real result. `Unknown` is the default and means "no result received yet", so the result dialog stays hidden. `Unrecognized` is the catch-all, so a status code that a later omnect-device-service adds still reaches the user. - `warning` counts as success in the UI: the reset completed, but a partition needed a second format attempt. `data_wiped` is shown only on a failed result, where the device may be partly wiped and needs another reset. ### wifi-commissioning-service probe via service-info - `GET /api/v1/service-info` (wifi-commissioning-service 0.2.0, omnect/wifi-commissioning-service#3) is the only availability probe: it returns version, interface name and BLE state in one call. The probe no longer calls `/api/v1/status` and `/api/v1/version`. - The version requirement works as for omnect-device-service: a semver requirement string (`>=0.2.1`) and the existing `VersionInfo { required, current, mismatch }`. `WifiAvailability::Unavailable` carries `version_info: Option<VersionInfo>`. `None` means the probe failed or the version does not parse; the device info shows `unknown`. An empty interface name also makes the service unavailable. - An old wifi-commissioning-service does not block the UI: WiFi is optional, so the mismatch shows only in the WiFi line of the device info. `ble_enabled` is only logged. ### Factory reset mode 4 removed - The web API no longer accepts factory-reset mode 4; a request with it is rejected when the body is deserialized. Modes 1 to 3 are unchanged. ## Reason With omnect/omnect-device-service#207, `error` is `null` after a successful reset, but omnect-ui requires a `String`. So every factory-reset result fails to deserialize, and the UI shows a parse error instead of the result. The status codes were renamed and extended in the same change, so the old `repr(u8)` enum with four variants cannot name what the device reports. The service-info probe needs one request instead of two. With the same requirement string and `VersionInfo` as omnect-device-service, both services report an unsupported version in the same way. The required version `>=0.2.1` is the released wifi-commissioning-service tag. Mode 4 ran a customer wipe script inside the scripted initramfs. omnect-os-init has no such script and answers mode 4 with `Invalid`, a reset that never runs. Rejecting the value here keeps the request from being sent at all. This PR, omnect/omnect-device-service#207 and omnect/meta-omnect#697 have to reach a release together, so no component offers a mode the device refuses, and the UI can read the result the device reports. --------- Signed-off-by: Jan Zachmann 50990105+JanZachmann@users.noreply.github.com
…697) ## Summary omnect-os moves from the scripted initramfs to [omnect-os-init](https://github.com/omnect/omnect-os-init), an init written in Rust. This PR is the main integration step: it builds the init, installs it as `/init` and removes the scripted initramfs (`init.d/*` from `omnect-os-initramfs-scripts`). - `omnect-os-init`: Cargo features follow the machine and distro (`grub` or `uboot`, the partition table from `OMNECT_PART_TABLE`, `factory-reset`, `resize-data`, `persistent-var-log`, `release-image`, `flash-mode-2`, `flash-mode-2-direct` when `OMNECT_FLASH_MODE_2_DIRECT_FLASHING` is `1`, `flash-mode-3`, `rtc` from the machine feature, and `early-modules` when `OMNECT_EARLY_MODULES` is set). The partition and bootloader constants that flash mode 1 needs are exported into the build; for flash mode 2 also the boot partition offset and size and `OMNECT_USER_ID`. - `OMNECT_PART_TABLE` (`gpt` or `dos`, default `gpt`) selects the partition table the init is built for; a machine whose wks file uses `--ptable msdos` sets `dos`. - `OMNECT_USER_ID` (uid and gid of the omnect user) is a distro variable, so the init recipe and `omnect_user.bbclass` share one value. - `omnect-os-initramfs`: installs the tools the init calls (`bash` for the debug shell on non-release images, `e2fsprogs`, `dosfstools`, `util-linux-sfdisk`, `util-linux-blkid` on GRUB machines, `efibootmgr` on EFI machines, `dhcpcd` and `base-passwd` for flash modes 2 and 3, `dropbear` and the omnect user for flash mode 2, `curl` and `ca-certificates` for flash mode 3). It drops the legacy script packages, the initramfs-framework modules, `os-release`, `jq`, `kmod`, `util-linux-uuidgen`, `bmaptool` and `xz`. `dhcpcd` is built without udev: it runs only in the initramfs, where udevd does not run, so it no longer pulls udev into the initramfs. The `fsck.fat` links in `/sbin` are created only when `/sbin` is not merged into `/usr/sbin`; on a merged image they replaced `fsck.fat` with a link to itself, so the boot partition was never checked. - GRUB passes `bootpart_fsuuid` instead of `rootblk`, so the init finds its disk by the filesystem UUID of the boot partition (partition 1, read with `probe`). A flash mode 1 clone gets a new boot volume ID ([omnect-os-init#38](omnect/omnect-os-init#38)); the rootfs UUID would be the same on the clone and its source. - GRUB boots from the disk it was loaded from (taken from `$cmdpath`, which needs the `regexp` module), not from `hd0`. `hd0` is the first disk of the firmware: with a flash mode 1 clone and its source both attached, GRUB read the grubenv of the clone but loaded kernel and rootfs from the source. - U-Boot declares every environment variable the OS writes at runtime as writable (`omnect_first_boot_done`, `omnect_fsck_*`, `omnect_factory_reset_last_error`, `flash-mode-url`, `flash-mode-url-sha256`), so a `saveenv` in `omnect_update_flow` keeps them. `data-mount-options` and `resized-data` are no longer listed, because the init reads neither. - `u-boot-scr`: the boot script no longer appends the literal text `None` to `bootargs` when `OMNECT_APPEND_1ST_BOOT` is unset, as on developer images. - `OMNECT_EARLY_MODULES` lists the kernel modules the init loads before `switch_root` (`imx_sdma` on phytec-imx8mm). It selects the `early-modules` feature, and the new package `omnect-os-init-early-modules` installs the list into the rootfs as `/etc/omnect/early-modules.d/omnect.conf`. The `omnect-os-init` recipe is built per machine, because its features and `build.rs` read machine settings. rpi kernels build compressed modules, so they get `CONFIG_MODULE_DECOMPRESS` when the variable is set. - `omnect-base-files` adds the mount point `/mnt/rootCurrentPrivate`, where the init bind-mounts the root filesystem. Its tmpfiles config creates `/mnt/cert/ca` and `/mnt/cert/priv` (`d` instead of `z`), because the init does not create them. - The busybox `poweroff` config is removed; the init powers off itself. - Removed with the scripted initramfs: - The recipe `omnect-os-initramfs-scripts`. A layer with a `.bbappend` for it no longer parses, and own `init.d` scripts are no longer possible. - Factory reset wipe mode 4, a custom wipe added by such a `.bbappend`. A request with `mode` 4 now gets status 1. - The bootloader variable `data-mount-options`; the data partition is always mounted with the default options. - An optional `preserve`: the factory-reset request must now contain it, else it gets status 3. - `omnect-device-service` reads the factory-reset result the init writes ([omnect-device-service#207](omnect/omnect-device-service#207)). - The expected bootloader checksums of genericx86-64, rpi and phytec-imx8mm follow the changed bootloader inputs. - README and `doc/`: keep the build settings and link to the [omnect-os-init docs](https://github.com/omnect/omnect-os-init/blob/main/docs/usage.md) for the behaviour of flash modes, factory reset, filesystem check and the status file. ## Reason The initramfs moves from shell scripts to one Rust binary, for typed error handling, a defined result contract towards omnect-device-service, and unit tests for the boot, factory-reset and flash paths. - The `omnect-os-init` recipe builds the released version 0.1.0. It contains [omnect-os-init#40](omnect/omnect-os-init#40), which keeps what the scripted initramfs did: `/run` without suid and device files, the links of `create-fs-links.txt`, the persistent logs in `/mnt/data/log`, and the restored directories with their owner and mode. - The `omnect-device-service` recipe builds the released version 0.46.1, which contains [omnect-device-service#207](omnect/omnect-device-service#207). - [omnect-ui#132](omnect/omnect-ui#132) reads the new factory-reset result and no longer offers mode 4. The three PRs have to reach a release together. - The bootloader version changes on every machine, because `omnect_env.h` and the GRUB config are bootloader inputs (on phytec-imx8mm the boot script is one too); devices get a bootloader update with this image. A layer whose bootloader checksum covers `recipes-bsp/u-boot/u-boot/*` needs a new expected value at the same time. --------- Signed-off-by: Jan Zachmann 50990105+JanZachmann@users.noreply.github.com
Closes #205
Summary
omnect-os moves from the scripted initramfs to omnect-os-init, an init written in Rust (omnect/meta-omnect#697). The init writes the factory-reset result to
/run/omnect-device-service/omnect-os-initramfs.jsonin a new JSON format. This PR makes omnect-device-service read this format and report it to the twin.factory_reset. A result under the old keyfactory-resetis unusable: it is logged as an error and reported asnull. Anullunder the old key is a normal boot.0success,1invalid,2error,3config error, and the new4warning (the reset worked, but a partition needed a secondmkfs). The status is au32, as omnect-os-init writes it. A status code this version does not know is reported as it is, and the raw result is logged as a warning.errorandcontextare optional,pathsdefaults to an empty list.data_wipedis required: a result without it is unusable and reported asnull, because a missing value must not read as a safe abort.data_wiped: on error it tells a safe abort from a failure after data was already wiped.errorandcontextasnullinstead of leaving them out: twin reports are merge patches, so a missing key would keep the value of a previous result.factory_resetfeature version is 4, because the reported result changed. The crate version is 0.46.1.Invalid, a reset that never runs.testfiles/positive/contractholds copies of the status files that omnect-os-init writes for every factory reset result (itstests/contract, fix: keep the behaviour of the scripted initramfs, remove a stale update validation config omnect-os-init#40); a test parses each one and compares every field.preserveaccepts only the reported keys.update_validation.mdlink the initramfs docs in omnect-os-init (docs/usage.md) instead of meta-omnect and the legacy scripts. The README documents the result fields, and thatpreserveis mandatory, as the parser already requires.update_validation.mdsays that omnect-os-init deletesupdate_validation_conf.jsonon every boot without update validation (fix: keep the behaviour of the scripted initramfs, remove a stale update validation config omnect-os-init#40), so a config written by hand is gone after the next reboot.Reason
The parser did not match the omnect-os-init format: it read the key
factory-resetand required anerrorstring. So the result of the init was unusable and never reached the twin. See #205 for the full list of differences.omnect-os-init tests that it writes exactly the contract files, and this repo tests that it parses them. So a format change on one side fails a test once the copies are updated.
omnect-ui reads the published result and must follow the new format and the removed mode 4: omnect/omnect-ui#132. The local web service keeps the channel
FactoryResetV1and the route/factory-reset/v1, although the payload changed and mode 4 is rejected. An omnect-ui without omnect/omnect-ui#132 gets no signal about this. The only safeguard is that this PR, omnect/meta-omnect#697 and omnect/omnect-ui#132 reach a release together; omnect-ui#132 also requires ods>=0.46.1.