From 0d07004604fc30c638cc914faa8b0eb666ef7d69 Mon Sep 17 00:00:00 2001 From: Kartik Angiras Date: Tue, 20 Jan 2026 17:02:22 +0530 Subject: [PATCH 1/4] add support for integration of includeos Signed-off-by: Kartik Angiras --- README.md | 1 + docs/unikernel-support.md | 66 ++++++++- pkg/unikontainers/unikernels/includeos.go | 169 ++++++++++++++++++++++ pkg/unikontainers/unikernels/unikernel.go | 3 + 4 files changed, 237 insertions(+), 2 deletions(-) create mode 100644 pkg/unikontainers/unikernels/includeos.go diff --git a/README.md b/README.md index e201f65e..d0e5a2d5 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,7 @@ supported VM/Sandbox monitors and unikernels: | Rumprun | Solo5-hvt, Solo5-spt | x86,aarch64 | Block/Devmapper | | Unikraft | QEMU, Firecracker | x86 | Initrd, 9pfs | | MirageOS | QEMU, Solo5-hvt, Solo5-spt | x86,aarch64 | Block/Devmapper | +| IncludeOS | QEMU, Solo5-hvt, Solo5-spt | x86,aarch64 | Block/Devmapper | | Mewz | QEMU | x86 | In-memory | | Linux | QEMU, Firecracker | x86 | Initrd, Block/Devmapper, 9pfs, Virtiofs | diff --git a/docs/unikernel-support.md b/docs/unikernel-support.md index 51ebfe78..a1f975d2 100644 --- a/docs/unikernel-support.md +++ b/docs/unikernel-support.md @@ -10,8 +10,12 @@ the cloud-native ecosystem. For that reason, `urunc` aims to support all the available unikernel frameworks and similar technologies. For the time being, `urunc` provides support for -[Unikraft](https://unikraft.org/) and -[Rumprun](https://github.com/cloudkernels/rumprun) unikernels. +[Unikraft](https://unikraft.org/), +[Rumprun](https://github.com/cloudkernels/rumprun), +[MirageOS](https://github.com/mirage/mirage), +[IncludeOS](https://github.com/includeos/IncludeOS), +[Mewz](https://github.com/Mewz-project/Mewz) and +[Linux](https://github.com/torvalds/linux) unikernels. ## Unikraft @@ -275,6 +279,64 @@ sudo nerdctl run -m 512M --rm -ti --runtime io.containerd.urunc.v2 harbor.nbfc.i > Note: As far as we understand, Mewz requires at least 512M of memory to properly boot. +## IncludeOS + +[IncludeOS](https://github.com/includeos/IncludeOS) is a unikernel framework +written in C++, designed for building fast, secure, and resource-efficient +cloud applications. [IncludeOS](https://github.com/includeos/IncludeOS) enables +developers to write C++ applications that compile directly into bootable +unikernels, eliminating the need for a traditional operating system. +[IncludeOS](https://github.com/includeos/IncludeOS) is particularly well-suited +for web services and cloud-native applications, providing a minimal attack +surface and excellent performance characteristics. + +[IncludeOS](https://github.com/includeos/IncludeOS) provides a modern C++ +standard library implementation and includes networking capabilities with TCP/IP +stack support. The framework focuses on simplicity and performance, making it +ideal for microservices and cloud functions. + +### VMMs and other sandbox monitors + +[IncludeOS](https://github.com/includeos/IncludeOS) can execute on top of +various hypervisors and monitors. It supports execution on +[Qemu](https://www.qemu.org/) and can also run on top of +[Solo5](https://github.com/Solo5/solo5), which provides portability across +different virtualization backends. [IncludeOS](https://github.com/includeos/IncludeOS) +can access the network through virtio-net in the case of +[Qemu](https://qemu.org) and using [Solo5](https://github.com/Solo5/solo5)'s +I/O interface in the case of [Solo5](https://github.com/Solo5/solo5). For +storage, [IncludeOS](https://github.com/includeos/IncludeOS) supports +block-based storage through virtio-block and +[Solo5](https://github.com/Solo5/solo5)'s I/O interface. + +### IncludeOS and `urunc` + +In the case of [IncludeOS](https://github.com/includeos/IncludeOS), `urunc` +provides support for both [Qemu](https://www.qemu.org/) and +[Solo5](https://github.com/Solo5/solo5) monitors (including +[Solo5-hvt](https://github.com/Solo5/solo5) and +[Solo5-spt](https://github.com/Solo5/solo5)). For all monitors, `urunc` allows +access to both network and block storage through the respective monitor's I/O +interface. + +For more information on packaging +[IncludeOS](https://github.com/includeos/IncludeOS) unikernels for `urunc`, +take a look at our [packaging](../package/) page. + +An example of [IncludeOS](https://github.com/includeos/IncludeOS) on top of +[Solo5-hvt](https://github.com/Solo5/solo5) with `urunc`: + +```bash +sudo nerdctl run --rm -ti --runtime io.containerd.urunc.v2 harbor.nbfc.io/nubificus/urunc/includeos-hvt:latest +``` + +An example of [IncludeOS](https://github.com/includeos/IncludeOS) on top of +[Qemu](https://qemu.org) with `urunc`: + +```bash +sudo nerdctl run --rm -ti --runtime io.containerd.urunc.v2 harbor.nbfc.io/nubificus/urunc/includeos-qemu:latest +``` + ## Linux [Linux](https://github.com/torvalds/linux) is maybe the most widely used kernel diff --git a/pkg/unikontainers/unikernels/includeos.go b/pkg/unikontainers/unikernels/includeos.go new file mode 100644 index 00000000..6a342c89 --- /dev/null +++ b/pkg/unikontainers/unikernels/includeos.go @@ -0,0 +1,169 @@ +// Copyright (c) 2023-2026, Nubificus LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unikernels + +import ( + "fmt" + "strings" + + "github.com/urunc-dev/urunc/pkg/unikontainers/types" +) + +const IncludeOSUnikernel string = "includeos" + +type IncludeOS struct { + Command string + Monitor string + Envs []string + Net IncludeOSNet + Block []IncludeOSBlock +} + +type IncludeOSNet struct { + Address string + Gateway string + Mask string +} + +type IncludeOSBlock struct { + ID string + HostPath string +} + +func (i *IncludeOS) CommandString() (string, error) { + // IncludeOS typically accepts command-line arguments + // Network configuration is usually done via the unikernel image itself + // or via command-line arguments depending on the application + cmdParts := []string{} + + // Add environment variables if any + for _, env := range i.Envs { + cmdParts = append(cmdParts, env) + } + + // Add network configuration if present + if i.Net.Address != "" { + cmdParts = append(cmdParts, fmt.Sprintf("--net=%s/%s", i.Net.Address, i.Net.Mask)) + if i.Net.Gateway != "" { + cmdParts = append(cmdParts, fmt.Sprintf("--gateway=%s", i.Net.Gateway)) + } + } + + // Add the main command + if i.Command != "" { + cmdParts = append(cmdParts, i.Command) + } + + return strings.Join(cmdParts, " "), nil +} + +func (i *IncludeOS) SupportsBlock() bool { + return true +} + +func (i *IncludeOS) SupportsFS(fsType string) bool { + switch fsType { + case "ext2", "ext3", "ext4": + return true + default: + return false + } +} + +func (i *IncludeOS) MonitorNetCli(ifName string, mac string) string { + switch i.Monitor { + case "hvt", "spt": + // Solo5 monitor options for networking + netOption := "--net:service=" + ifName + netOption += " --net-mac:service=" + mac + return netOption + case "qemu": + // QEMU handles networking through its own options + // This is typically configured in the hypervisor layer + return "" + default: + return "" + } +} + +func (i *IncludeOS) MonitorBlockCli() []types.MonitorBlockArgs { + if len(i.Block) == 0 { + return nil + } + + switch i.Monitor { + case "hvt", "spt": + // Solo5 monitors support block devices with specific IDs + blockArgs := make([]types.MonitorBlockArgs, 0, len(i.Block)) + for _, blk := range i.Block { + // Use the first block device or a default ID + id := blk.ID + if id == "" { + id = "storage" + } + blockArgs = append(blockArgs, types.MonitorBlockArgs{ + ID: id, + Path: blk.HostPath, + }) + } + // Solo5 typically supports a single block device, so return the first one + if len(blockArgs) > 0 { + return blockArgs[:1] + } + return blockArgs + case "qemu": + // QEMU handles block devices through its own options + // This is typically configured in the hypervisor layer + return nil + default: + return nil + } +} + +func (i *IncludeOS) MonitorCli() types.MonitorCliArgs { + // IncludeOS does not require any generic monitor-specific arguments + return types.MonitorCliArgs{} +} + +func (i *IncludeOS) Init(data types.UnikernelParams) error { + // Initialize network configuration if provided + if data.Net.Mask != "" { + i.Net.Address = data.Net.IP + i.Net.Gateway = data.Net.Gateway + i.Net.Mask = data.Net.Mask + } + + // Initialize block devices if provided + i.Block = make([]IncludeOSBlock, 0, len(data.Block)) + for _, blk := range data.Block { + newBlk := IncludeOSBlock{ + ID: blk.ID, + HostPath: blk.Source, + } + i.Block = append(i.Block, newBlk) + } + + // Set command line and environment variables + i.Command = strings.Join(data.CmdLine, " ") + i.Monitor = data.Monitor + i.Envs = data.EnvVars + + return nil +} + +func newIncludeOS() *IncludeOS { + includeosStruct := new(IncludeOS) + return includeosStruct +} diff --git a/pkg/unikontainers/unikernels/unikernel.go b/pkg/unikontainers/unikernels/unikernel.go index d1f57cac..58b44915 100644 --- a/pkg/unikontainers/unikernels/unikernel.go +++ b/pkg/unikontainers/unikernels/unikernel.go @@ -39,6 +39,9 @@ func New(unikernelType string) (types.Unikernel, error) { case LinuxUnikernel: unikernel := newLinux() return unikernel, nil + case IncludeOSUnikernel: + unikernel := newIncludeOS() + return unikernel, nil default: return nil, ErrNotSupportedUnikernel } From 50da1f358bed79ffc1b285e46fad1341ac0032f1 Mon Sep 17 00:00:00 2001 From: Kartik Angiras Date: Tue, 20 Jan 2026 17:10:08 +0530 Subject: [PATCH 2/4] update unikernel-support docs Signed-off-by: Kartik Angiras --- docs/unikernel-support.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/unikernel-support.md b/docs/unikernel-support.md index a1f975d2..a6eb634a 100644 --- a/docs/unikernel-support.md +++ b/docs/unikernel-support.md @@ -11,11 +11,9 @@ available unikernel frameworks and similar technologies. For the time being, `urunc` provides support for [Unikraft](https://unikraft.org/), -[Rumprun](https://github.com/cloudkernels/rumprun), -[MirageOS](https://github.com/mirage/mirage), -[IncludeOS](https://github.com/includeos/IncludeOS), -[Mewz](https://github.com/Mewz-project/Mewz) and -[Linux](https://github.com/torvalds/linux) unikernels. +[Rumprun](https://github.com/cloudkernels/rumprun) and +[IncludeOS](https://github.com/includeos/IncludeOS) + unikernels. ## Unikraft From f8e9ed77312acb889644f7d1e3ed17f9a1030c82 Mon Sep 17 00:00:00 2001 From: Kartik Angiras Date: Tue, 20 Jan 2026 18:01:37 +0530 Subject: [PATCH 3/4] add test cases for network configuration Signed-off-by: Kartik Angiras --- pkg/unikontainers/unikernels/includeos.go | 52 ++++++------- .../unikernels/includeos_test.go | 75 +++++++++++++++++++ 2 files changed, 102 insertions(+), 25 deletions(-) create mode 100644 pkg/unikontainers/unikernels/includeos_test.go diff --git a/pkg/unikontainers/unikernels/includeos.go b/pkg/unikontainers/unikernels/includeos.go index 6a342c89..6ade6837 100644 --- a/pkg/unikontainers/unikernels/includeos.go +++ b/pkg/unikontainers/unikernels/includeos.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -43,25 +43,33 @@ type IncludeOSBlock struct { } func (i *IncludeOS) CommandString() (string, error) { - // IncludeOS typically accepts command-line arguments - // Network configuration is usually done via the unikernel image itself - // or via command-line arguments depending on the application cmdParts := []string{} - // Add environment variables if any - for _, env := range i.Envs { - cmdParts = append(cmdParts, env) - } - - // Add network configuration if present + // IncludeOS expects network config as a JSON string argument. + // We construct this manually to ensure it matches the specific schema required by the OS. if i.Net.Address != "" { - cmdParts = append(cmdParts, fmt.Sprintf("--net=%s/%s", i.Net.Address, i.Net.Mask)) + // Default to iface 0. + // Constructing JSON: {"net":[{"iface":0,"address":"...","netmask":"...","gateway":"..."}]} + gwPart := "" if i.Net.Gateway != "" { - cmdParts = append(cmdParts, fmt.Sprintf("--gateway=%s", i.Net.Gateway)) + gwPart = fmt.Sprintf(`,"gateway":"%s"`, i.Net.Gateway) } + + // We assume i.Net.Mask is in dotted-decimal format (e.g 255.255.255.0) + // If urunc provides CIDR conversion logic would be needed here + jsonConfig := fmt.Sprintf( + `{"net":[{"iface":0,"address":"%s","netmask":"%s"%s}]}`, + i.Net.Address, + i.Net.Mask, + gwPart, + ) + cmdParts = append(cmdParts, jsonConfig) + } + + if len(i.Envs) > 0 { + cmdParts = append(cmdParts, i.Envs...) } - // Add the main command if i.Command != "" { cmdParts = append(cmdParts, i.Command) } @@ -90,8 +98,7 @@ func (i *IncludeOS) MonitorNetCli(ifName string, mac string) string { netOption += " --net-mac:service=" + mac return netOption case "qemu": - // QEMU handles networking through its own options - // This is typically configured in the hypervisor layer + // QEMU handles networking through its own options in the hypervisor layer return "" default: return "" @@ -105,10 +112,10 @@ func (i *IncludeOS) MonitorBlockCli() []types.MonitorBlockArgs { switch i.Monitor { case "hvt", "spt": - // Solo5 monitors support block devices with specific IDs + // Solo5 monitors support block devices with specific IDs. + // Note: Solo5 typically supports a single block device. blockArgs := make([]types.MonitorBlockArgs, 0, len(i.Block)) for _, blk := range i.Block { - // Use the first block device or a default ID id := blk.ID if id == "" { id = "storage" @@ -118,14 +125,13 @@ func (i *IncludeOS) MonitorBlockCli() []types.MonitorBlockArgs { Path: blk.HostPath, }) } - // Solo5 typically supports a single block device, so return the first one + // Return only the first block device to ensure compatibility with Solo5 if len(blockArgs) > 0 { return blockArgs[:1] } return blockArgs case "qemu": // QEMU handles block devices through its own options - // This is typically configured in the hypervisor layer return nil default: return nil @@ -138,14 +144,12 @@ func (i *IncludeOS) MonitorCli() types.MonitorCliArgs { } func (i *IncludeOS) Init(data types.UnikernelParams) error { - // Initialize network configuration if provided if data.Net.Mask != "" { i.Net.Address = data.Net.IP i.Net.Gateway = data.Net.Gateway i.Net.Mask = data.Net.Mask } - // Initialize block devices if provided i.Block = make([]IncludeOSBlock, 0, len(data.Block)) for _, blk := range data.Block { newBlk := IncludeOSBlock{ @@ -155,7 +159,6 @@ func (i *IncludeOS) Init(data types.UnikernelParams) error { i.Block = append(i.Block, newBlk) } - // Set command line and environment variables i.Command = strings.Join(data.CmdLine, " ") i.Monitor = data.Monitor i.Envs = data.EnvVars @@ -164,6 +167,5 @@ func (i *IncludeOS) Init(data types.UnikernelParams) error { } func newIncludeOS() *IncludeOS { - includeosStruct := new(IncludeOS) - return includeosStruct -} + return &IncludeOS{} +} \ No newline at end of file diff --git a/pkg/unikontainers/unikernels/includeos_test.go b/pkg/unikontainers/unikernels/includeos_test.go new file mode 100644 index 00000000..ef96ae38 --- /dev/null +++ b/pkg/unikontainers/unikernels/includeos_test.go @@ -0,0 +1,75 @@ +package unikernels + +import ( + "strings" + "testing" +) + +func TestIncludeOS_CommandString(t *testing.T) { + tests := []struct { + name string + unikernel *IncludeOS + expected []string + }{ + { + name: "Full Configuration", + unikernel: &IncludeOS{ + Command: "my_app_arg", + Envs: []string{"FOO=bar", "BAZ=qux"}, + Net: IncludeOSNet{ + Address: "192.168.1.5", + Mask: "255.255.255.0", + Gateway: "192.168.1.1", + }, + }, + expected: []string{ + `{"net":[{"iface":0,"address":"192.168.1.5","netmask":"255.255.255.0","gateway":"192.168.1.1"}]}`, + "FOO=bar", + "BAZ=qux", + "my_app_arg", + }, + }, + { + name: "Network Only (No Gateway)", + unikernel: &IncludeOS{ + Net: IncludeOSNet{ + Address: "10.0.0.2", + Mask: "255.255.0.0", + }, + }, + expected: []string{ + `{"net":[{"iface":0,"address":"10.0.0.2","netmask":"255.255.0.0"}]}`, + }, + }, + { + name: "No Network (Command Only)", + unikernel: &IncludeOS{ + Command: "just_running", + }, + expected: []string{ + "just_running", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.unikernel.CommandString() + if err != nil { + t.Fatalf("CommandString() error = %v", err) + } + + for _, part := range tt.expected { + if !strings.Contains(got, part) { + t.Errorf("CommandString() = %v\nMissing expected part: %v", got, part) + } + } + + if tt.name == "No Network (Command Only)" { + if strings.Contains(got, "net") { + t.Errorf("CommandString() should not contain network config, got: %v", got) + } + } + }) + } +} From 5aa312435b96be2cc16e6fafe519b3fa7820a4d5 Mon Sep 17 00:00:00 2001 From: Kartik Angiras Date: Tue, 20 Jan 2026 18:06:17 +0530 Subject: [PATCH 4/4] fix: fixes lint failures Signed-off-by: Kartik Angiras --- pkg/unikontainers/unikernels/includeos.go | 6 +++--- pkg/unikontainers/unikernels/includeos_test.go | 13 +++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/pkg/unikontainers/unikernels/includeos.go b/pkg/unikontainers/unikernels/includeos.go index 6ade6837..e4b8effc 100644 --- a/pkg/unikontainers/unikernels/includeos.go +++ b/pkg/unikontainers/unikernels/includeos.go @@ -1,10 +1,10 @@ -// Copyright (c) 2023-2026, Nubificus LTD +// Copyright (c) 2023-2025, Nubificus LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -168,4 +168,4 @@ func (i *IncludeOS) Init(data types.UnikernelParams) error { func newIncludeOS() *IncludeOS { return &IncludeOS{} -} \ No newline at end of file +} diff --git a/pkg/unikontainers/unikernels/includeos_test.go b/pkg/unikontainers/unikernels/includeos_test.go index ef96ae38..3ce78ec0 100644 --- a/pkg/unikontainers/unikernels/includeos_test.go +++ b/pkg/unikontainers/unikernels/includeos_test.go @@ -1,3 +1,16 @@ +// Copyright (c) 2023-2025, Nubificus LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package unikernels import (