Skip to content

fix(sysctl): decide from the drop-in, not from the record - #567

Merged
retr0h merged 1 commit into
mainfrom
fix/sysctl-decides-from-the-host
Oct 1, 2026
Merged

retr0h merged 1 commit into
mainfrom
fix/sysctl-decides-from-the-host

Conversation

@retr0h

@retr0h retr0h commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator

Closes #564.

Create returned Changed: false whenever a KV state entry existed with no UndeployedAt, without reading the host and without comparing the value it was asked for.

deploy already compares against the file on disk and has a comment saying so. The pre-check was the only thing standing between a request and the right answer, so the fix is deleting it.

Three cases it got wrong

  • a drop-in edited by hand stood, because the record still said managed
  • a deleted drop-in was never restored, same reason
  • create with a different value on a managed key reported no change and left the old value in place

The third needed nobody to touch the host. Create(key, "1") then Create(key, "0") returned Changed: false both times and left 1.

The test asserted the defect

name: "when key already managed returns unchanged",
entry: sysctl.Entry{Key: "net.ipv4.ip_forward", Value: "1"},
setup: func() {
    stateBytes := managedStateJSON("net.ipv4.ip_forward", "0", ...)
    ...
},
validateFunc: func(result *sysctl.CreateResult, err error) {
    suite.False(result.Changed)
},

The record holds 0, the request asks for 1, and it expects no change. That is the bug written down as the specification.

Replaced with four cases: unchanged content, a changed value, a hand edit, and a deletion. Each asserts the file contents afterwards rather than only the Changed flag.

Proof they catch it

$ git stash -- internal/provider/node/sysctl/debian.go   # old implementation
$ go test ./internal/provider/node/sysctl/ -run TestCreate
10 failures

$ git stash pop                                           # with the fix
ok  github.com/osapi-io/osapi/internal/provider/node/sysctl

Consistency

Every other provider already decides this way: file, ntp and netplan hash the file on disk, user reads authorized_keys, and cron, certificate and service delegate to fileDeployer. sysctl was the only one left.

ntp and cron both say so in their own comments. sysctl's said "returns Changed: false if the key is already managed", which was the bug stated as intent. It now reads like theirs.

Contract: osapi-io/specs#238.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FuKUsHFG1EqZXamffh9M2c

Create returned Changed: false whenever a KV state entry existed with no
UndeployedAt, without reading the host and without comparing the value it
was asked for. deploy already compares against the file on disk, so the
pre-check was the only thing standing between a request and the right
answer.

Three cases it got wrong. A drop-in edited by hand stood, because the record
still said managed. A deleted drop-in was never restored, for the same
reason. And create with a different value on a managed key reported no
change and left the old value in place, which needed nobody to touch the
host at all.

The test that covered this asserted the defect: the record held 0, the
request asked for 1, and it expected Changed: false. It is replaced by four
cases covering unchanged content, a changed value, a hand edit and a
deletion. Against the previous implementation they fail ten times.

Every other provider already decides this way. ntp and cron say so in their
own comments.

Closes #564

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FuKUsHFG1EqZXamffh9M2c
@codecov

codecov Bot commented Oct 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #567      +/-   ##
==========================================
- Coverage   99.95%   99.95%   -0.01%     
==========================================
  Files         501      501              
  Lines       24084    24073      -11     
==========================================
- Hits        24074    24063      -11     
  Misses         10       10              
Files with missing lines Coverage Δ
internal/provider/node/sysctl/debian.go 100.00% <ø> (ø)

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f829406...af0f4ab. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@retr0h
retr0h merged commit 0423785 into main Oct 1, 2026
12 checks passed
@retr0h
retr0h deleted the fix/sysctl-decides-from-the-host branch October 1, 2026 18:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sysctl decides idempotency from its own record, so drift is never corrected

1 participant