feat(unikernels): pass OCI rlimits to urunit config#558
feat(unikernels): pass OCI rlimits to urunit config#558namansh70747 wants to merge 4 commits intourunc-dev:mainfrom
Conversation
✅ Deploy Preview for urunc canceled.
|
There was a problem hiding this comment.
Pull request overview
This PR forwards OCI process.rlimits into the generated urunit.conf for Linux unikernels so urunit can apply resource limits before starting the target application.
Changes:
- Extend
types.ProcessConfigwithRlimits []specs.POSIXRlimitand populate it from the OCI spec duringExec(). - Emit one
RLIMIT:TYPE:SOFT:HARDline per rlimit inLinux.buildUrunitConfig(). - Add unit tests around
buildUrunitConfig()rlimit serialization.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/unikontainers/unikontainers.go | Plumbs OCI Process.Rlimits into the per-guest ProcessConfig. |
| pkg/unikontainers/unikernels/linux.go | Serializes ProcessConfig.Rlimits into urunit.conf as RLIMIT: lines. |
| pkg/unikontainers/unikernels/linux_test.go | Adds tests covering rlimit presence/placement in generated config. |
| pkg/unikontainers/types/types.go | Adds Rlimits field to ProcessConfig and imports OCI runtime-spec types. |
| .github/linters/urunc-dict.txt | Updates linter dictionary for new rlimits/Rlimits tokens. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ESRCH | ||
| Prafful | ||
| praffq No newline at end of file | ||
| praffqrlimits |
There was a problem hiding this comment.
The custom dictionary entry appears to have accidentally concatenated two words: praffqrlimits. This won’t whitelist the intended lowercase rlimits token (and also removes the previous praffq entry). Split this into separate lines so rlimits is present as its own dictionary word (and keep/remove praffq intentionally).
| praffqrlimits | |
| praffq | |
| rlimits |
| l := newTestLinux(nil) | ||
| conf := l.buildUrunitConfig() | ||
| t.Logf("generated urunit.conf:\n%s", conf) | ||
|
|
||
| if strings.Contains(conf, "RLIMIT:") { | ||
| t.Fatalf("expected no RLIMIT lines, got:\n%s", conf) |
There was a problem hiding this comment.
The PR description mentions unit tests for both nil and empty rlimit slices, but the “no rlimits” test only covers nil. Add an explicit empty-slice case (e.g., []specs.POSIXRlimit{}) to ensure empty-but-non-nil input also produces no RLIMIT: lines.
| l := newTestLinux(nil) | |
| conf := l.buildUrunitConfig() | |
| t.Logf("generated urunit.conf:\n%s", conf) | |
| if strings.Contains(conf, "RLIMIT:") { | |
| t.Fatalf("expected no RLIMIT lines, got:\n%s", conf) | |
| tests := []struct { | |
| name string | |
| rlimits []specs.POSIXRlimit | |
| }{ | |
| {name: "nil", rlimits: nil}, | |
| {name: "empty", rlimits: []specs.POSIXRlimit{}}, | |
| } | |
| for _, tt := range tests { | |
| t.Run(tt.name, func(t *testing.T) { | |
| l := newTestLinux(tt.rlimits) | |
| conf := l.buildUrunitConfig() | |
| t.Logf("generated urunit.conf:\n%s", conf) | |
| if strings.Contains(conf, "RLIMIT:") { | |
| t.Fatalf("expected no RLIMIT lines, got:\n%s", conf) | |
| } | |
| }) |
dc160a1 to
5536349
Compare
Parse RLIMIT:TYPE:SOFT:HARD entries from the UCS block and apply them via setrlimit(2) before privilege drop in setup_exec_env(). The type string (e.g. RLIMIT_NOFILE) is resolved to a POSIX resource integer using a static lookup table. Limits are stored in two parallel dynamically allocated arrays inside struct process_config: rlimits[] for the struct rlimit values and rlimit_resources[] for the resource integers. Both are freed on cleanup. This is the urunit side of the OCI rlimits feature. The urunc side that serializes these entries is in urunc-dev/urunc#558. Signed-off-by: namansh70747 <namansh70747@gmail.com>
Parse RLIMIT:TYPE:SOFT:HARD entries from the UCS block and apply them via setrlimit(2) before privilege drop in setup_exec_env(). The type string (e.g. RLIMIT_NOFILE) is resolved to a POSIX resource integer using a static lookup table. Limits are stored in two parallel dynamically allocated arrays inside struct process_config: rlimits[] for the struct rlimit values and rlimit_resources[] for the resource integers. Both are freed on cleanup. This is the urunit side of the OCI rlimits feature. The urunc side that serializes these entries is in urunc-dev/urunc#558. Signed-off-by: namansh70747 <namansh70747@gmail.com>
Parse RLIMIT:TYPE:SOFT:HARD entries from the UCS block and apply them via setrlimit(2) before privilege drop in setup_exec_env(). The type string (e.g. RLIMIT_NOFILE) is resolved to a POSIX resource integer using a static lookup table. Limits are stored in two parallel dynamically allocated arrays inside struct process_config: rlimits[] for the struct rlimit values and rlimit_resources[] for the resource integers. Both are freed on cleanup. This is the urunit side of the OCI rlimits feature. The urunc side that serializes these entries is in urunc-dev/urunc#558. Signed-off-by: namansh70747 <namansh70747@gmail.com>
|
Companion urunit PR is now open: nubificus/urunit#14 |
|
Hello @namansh70747 , thank you for this PR. Two things:
|
|
Hello @namansh70747 , is there any reason for not running the tests and linter locally? |
|
|
||
| block := conf[ucs : uce+4] | ||
| want := "RLIMIT:RLIMIT_NOFILE:1024:4096\n" | ||
| if !strings.Contains(block, want) { |
There was a problem hiding this comment.
For consistency, I’d suggest using the assert package from testify, as this is how assertions are handled in other tests.
| } | ||
| } | ||
|
|
||
| func TestBuildUrunitConfigPrintFull(t *testing.T) { |
There was a problem hiding this comment.
Looks like this isn’t making any assertions, so I’m not sure it’s worth keeping. It also seems the logic is already covered by the tests above, so we could consider removing it.
5536349 to
5eb84df
Compare
|
Hello @cmainas,
I’ve also added screenshots of it in the updated PR description. |
Serialize process.rlimits from the OCI spec into the URUNIT_CONFIG file as RLIMIT:TYPE:SOFT:HARD lines inside the UCS block. Fixes: urunc-dev#312 Signed-off-by: namansh70747 <namsh70747@gmail.com> Signed-off-by: Sankalp <sankalp25103@gmail.com>
5eb84df to
6fcd272
Compare
Hello @namansh70747 , thank you for running the linter. However, the tests you reference are the unit tests. We also have some end-to-end tests that can be executed with |
Hi, thank you for the feedback. I have performed end-to-end testing for this PR. Since the changes touch the Linux unikernel path (specifically To also verify the urunit side (the PR I referenced), I built the I ran the tests with The three tests I ran against the custom image are attached to this comment:
All 3 passed, 0 failed. Regarding the remaining test cases in the suite: the HVT tests fail due to a pre-existing seccomp filter stacking issue unrelated to this change (reproducible on the main branch as well), and the Qemu-unikraft/Firecracker-unikraft tests do not go through Let me know if you need any additional information.
|
|
Hello @namansh70747 , thank you for the extra information and the tests you executed.
Just a note, the Linux images in the tests that do not end in raw (e.g. initrd) do not use the container's rootfs as the rootfs for the sandbox, therefore the COPY inside the container's rootfs will not replace the urunit binary.
We need to open a new issue for this.
I am not sure what you mean non-KVM dependent. Qemu uses KVM too.
The above tests showcase that the changes in this PR do not break the existing functionality of |



Description
Forward OCI
process.rlimitsinto the generatedURUNIT_CONFIGusedfor Linux unikernels, so the guest init process (
urunit) can enforceresource limits via
setrlimit(2)before starting the target application.The
URUNIT_CONFIGis passed as an initrd to the guest VM and parsedby
urunitfrom the UCS block. Each rlimit entry is serialized as aRLIMIT:TYPE:SOFT:HARDline, following the same pattern used forUID,GID, andWDintroduced in #308.uruncreadsu.Spec.Process.Rlimits, stores them in a newRlimits []specs.POSIXRlimitfield intypes.ProcessConfig, andserializes them in
buildUrunitConfig(). The companion urunit PRthat parses and applies these entries via
setrlimit(2)beforeprivilege drop is nubificus/urunit#14.
The implementation was developed independently, with reference to #353
for understanding the existing issue scope.
Related issues
How was this tested?
Executed in a Lima ARM64 VM (macOS host, Docker installed inside VM).
Build and install:
Lint:
Unit tests:
go test -v -count=1 ./pkg/unikontainers/unikernels/go vet:
LLM usage
N/A
Checklist
make lint).make test_ctr,make test_nerdctl,make test_docker,make test_crictl).(Lima VM does not expose
/dev/kvm, so HVT-backed tests are blocked. The three Qemu-linuxCtr tests were run against a custom image with the
feat_rlimitsurunit binary — all passed.attached in comments)
Signed-off-by: Naman Sharma namsh70747@gmail.com
Signed-off-by: Sankalp sankalp25103@gmail.com