Skip to content

refactor: introduce Cgroup interface and consolidate cgroupfs operations#23

Merged
ternbusty merged 1 commit into
mainfrom
refactor/cgroup-interface
May 9, 2026
Merged

refactor: introduce Cgroup interface and consolidate cgroupfs operations#23
ternbusty merged 1 commit into
mainfrom
refactor/cgroup-interface

Conversation

@ternbusty
Copy link
Copy Markdown
Owner

Summary

Same pattern as the Syscall (#19 / #20) and FileSystem (#22) refactors but for the cgroup operations.

  • New src/nativeMain/kotlin/cgroup/Cgroup.kt interface with setup / cleanup / getPids.
  • New src/nativeMain/kotlin/cgroup/CgroupV2.kt is the cgroupfs-backed implementation, holds FileSystem in its constructor. Preserves the existing logic verbatim — the subtree_control ancestor walk (#17 fix), v1-shares-to-v2-weight conversion, swap.max = swap - limit semantic.
  • utils/FileSystem.kt gains removeDirectory(path: String): Boolean so cgroup cleanup can stop calling platform.posix.rmdir/access directly. RealFileSystem implements it via posix rmdir; FakeFileSystem mutates its in-memory directories set.
  • Main.kt instantiates CgroupV2(fs) once and threads it into create / delete / ps. start / kill / state don't need it.

Tests

  • cgroup/CgroupTest.kt is migrated from setupCgroup(fs, ...) to CgroupV2(fs).setup(...). Three new cleanup tests cover the null-path no-op and FakeFileSystem-backed directory removal.
  • cgroup/FakeCgroup.kt (new) records every call. It's available for the next round of higher-layer tests (e.g. asserting that delete invokes cgroup.cleanup for the right path).

Test plan

  • build workflow passes (compile + ktlint + kotest, including the migrated cgroup tests)
  • integration workflow still passes — production-side this is a name change plus indirection through Cgroup / CgroupV2; the actual cgroupfs operations are unchanged

Promotes the cgroup helpers in cgroup/Cgroup.kt to a Cgroup interface
with a CgroupV2 implementation that holds FileSystem in its constructor.
Higher layers (Create, Delete, Ps, runMainProcess) now take a Cgroup
parameter alongside Syscall/FileSystem and call cgroup.setup / cleanup /
getPids instead of free helpers.

This is the same pattern as Syscall (#19/#20) and FileSystem (#22):
domain code goes through an injected interface so tests can substitute
a fake without touching the real cgroupfs.

What changes:
- cgroup/Cgroup.kt: now just the interface (setup, cleanup, getPids).
- cgroup/CgroupV2.kt (new): implementation. Keeps the existing logic
  including the subtree_control ancestor walk (#17 fix), the cgroup v1
  shares -> cgroup v2 weight conversion, the swap.max = swap - limit
  semantic, and the procs write.
- utils/FileSystem.kt gains removeDirectory(path: String): Boolean so
  cgroup cleanup can drop its direct platform.posix.rmdir/access usage.
  RealFileSystem implements it via posix rmdir, FakeFileSystem mutates
  its in-memory directories set.
- Main.kt instantiates CgroupV2(fs) once and threads it into create /
  delete / ps. start / kill / state don't need it.
- Tests:
  - CgroupTest now exercises CgroupV2 directly. New cleanup tests cover
    the no-op-for-null path and FakeFileSystem-backed directory removal.
  - FakeCgroup (new) records calls; available for upcoming higher-layer
    tests of Create / Delete / Ps.

No behavior change is intended; integration workflow exercises mount,
pivot_root, capabilities, seccomp, and cgroup limits end-to-end.
@ternbusty ternbusty merged commit 1a05e3f into main May 9, 2026
2 checks passed
@ternbusty ternbusty deleted the refactor/cgroup-interface branch May 9, 2026 22:36
ternbusty added a commit that referenced this pull request May 9, 2026
…impls

Same pattern as Syscall (#19/#20), FileSystem (#22), and Cgroup (#23) but
for IPC. The Main / Init senders/receivers and the NotifyListener /
NotifySocket pair become interfaces so domain code can be tested with
in-memory fakes that record sent messages and let tests preseed received
ones.

What changes:
- channel/Channel.kt now declares the four message-channel interfaces
  (MainSender, MainReceiver, InitSender, InitReceiver). Each keeps fd()
  and close() so the FD-inheritance and lifecycle wiring don't need a
  separate seam.
- channel/SocketChannel.kt (new) holds the Unix-socketpair-backed
  implementations (SocketMainSender etc.) and the existing private
  helpers (createSocketPair, sendMessage, sendMessageWithFd,
  receiveMessageWithFd). The mainChannel / initChannel factories return
  the interface types but instantiate the Socket* classes.
- channel/NotifySocket.kt now declares NotifyListener and NotifySocket
  as interfaces, with SocketNotifyListener (two constructors: from path
  and from inherited FD) and SocketNotifySocket as the impls.
- Construction sites (Main.kt for the init branch, Create.kt for the
  initial NotifyListener, Start.kt for the client) reference the Socket*
  impls directly. Everything else holds the interface type.

Tests:
- channel/FakeChannel.kt provides FakeMainSender, FakeMainReceiver,
  FakeInitSender, FakeInitReceiver, FakeNotifyListener,
  FakeNotifySocket. Senders record every call; receivers consume from
  preseeded queues.
- channel/FakeChannelTest.kt is a sanity check that the fakes record
  calls and that receivers throw IllegalStateException when an
  expected message wasn't preseeded — the contract upcoming
  higher-layer tests will rely on.

No production behavior change. The integration workflow exercises the
real channel flow end-to-end.
ternbusty added a commit that referenced this pull request May 9, 2026
…impls (#24)

Same pattern as Syscall (#19/#20), FileSystem (#22), and Cgroup (#23) but
for IPC. The Main / Init senders/receivers and the NotifyListener /
NotifySocket pair become interfaces so domain code can be tested with
in-memory fakes that record sent messages and let tests preseed received
ones.

What changes:
- channel/Channel.kt now declares the four message-channel interfaces
  (MainSender, MainReceiver, InitSender, InitReceiver). Each keeps fd()
  and close() so the FD-inheritance and lifecycle wiring don't need a
  separate seam.
- channel/SocketChannel.kt (new) holds the Unix-socketpair-backed
  implementations (SocketMainSender etc.) and the existing private
  helpers (createSocketPair, sendMessage, sendMessageWithFd,
  receiveMessageWithFd). The mainChannel / initChannel factories return
  the interface types but instantiate the Socket* classes.
- channel/NotifySocket.kt now declares NotifyListener and NotifySocket
  as interfaces, with SocketNotifyListener (two constructors: from path
  and from inherited FD) and SocketNotifySocket as the impls.
- Construction sites (Main.kt for the init branch, Create.kt for the
  initial NotifyListener, Start.kt for the client) reference the Socket*
  impls directly. Everything else holds the interface type.

Tests:
- channel/FakeChannel.kt provides FakeMainSender, FakeMainReceiver,
  FakeInitSender, FakeInitReceiver, FakeNotifyListener,
  FakeNotifySocket. Senders record every call; receivers consume from
  preseeded queues.
- channel/FakeChannelTest.kt is a sanity check that the fakes record
  calls and that receivers throw IllegalStateException when an
  expected message wasn't preseeded — the contract upcoming
  higher-layer tests will rely on.

No production behavior change. The integration workflow exercises the
real channel flow end-to-end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant