Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/contributors.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,6 @@ users:
Inuth0603:
name: Divyam Agarwal
email: iitdivyamagarwal@gmail.com
indrajithek:
name: Indrajith
email: 29561593+indrajithek@users.noreply.github.com
4 changes: 3 additions & 1 deletion docs/tutorials/existing-container-linux.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ designed specifically for `urunc`. It performs the following actions:
the container in a Kubernetes cluster, where there is a high chance that
the gateway of the container might be in a different subnet than the IP.
As a result, the Linux kernel will fail to set the gateway.
2. Groups multi-word arguments correctly.
2. Reads the application command and its arguments from the configuration.
3. Mounts `/proc` and `/sys`.
4. Reads and sets the environment variables for the application execution
5. Reads and sets the uid,gid and working directory for the application execution
Expand All @@ -95,6 +95,8 @@ UCS
UID:<uid>
GID:<gid>
WD:<working_dir>
ARC:<number of arguments>
ARV:<argument> /* one line per argument */
UCE
```

Expand Down
16 changes: 15 additions & 1 deletion pkg/unikontainers/unikernels/linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const (
type Linux struct {
App string
Command string
Args []string // application command, verbatim, for urunit
Monitor string
Env []string
Net LinuxNet
Expand Down Expand Up @@ -123,7 +124,11 @@ func (l *Linux) CommandString() (string, error) {
bootParams += " URUNIT_DEFROUTE=1"
}
if l.App != "" {
initParams := rdinit + "init=" + l.App + " -- " + l.Command
initParams := rdinit + "init=" + l.App
// urunit takes the application command from its configuration
if !l.InitrdConf {
initParams += " -- " + l.Command
}
bootParams += " " + initParams
}

Expand Down Expand Up @@ -275,6 +280,7 @@ func (l *Linux) parseCmdLine(cmdLine []string) error {
}

l.App = normalizedArgs[0]
l.Args = cmdLine[1:]
if len(normalizedArgs) > 1 {
l.Command = strings.Join(normalizedArgs[1:], " ")
} else {
Expand Down Expand Up @@ -337,6 +343,14 @@ func (l *Linux) buildUrunitConfig() string {
sb.WriteString("WD:")
sb.WriteString(l.ProcConfig.WorkDir)
sb.WriteString("\n")
sb.WriteString("ARC:")
sb.WriteString(strconv.Itoa(len(l.Args)))
sb.WriteString("\n")
for _, arg := range l.Args {
sb.WriteString("ARV:")
sb.WriteString(arg)
sb.WriteString("\n")
}
sb.WriteString(lpcEndMarker)
sb.WriteString("\n")
sb.WriteString(blkStartMarker)
Expand Down
82 changes: 82 additions & 0 deletions pkg/unikontainers/unikernels/linux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// 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 (
"strconv"
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TestLinuxUrunitArgs covers the arguments that the kernel command line
// mangles: an empty argument and a literal "--".
func TestLinuxUrunitArgs(t *testing.T) {
t.Parallel()

testCases := []struct {
name string
cmdLine []string
expected string
}{
{
name: "empty argument",
cmdLine: []string{"/urunit", "/app", "a", "", "b"},
expected: "ARC:4\nARV:/app\nARV:a\nARV:\nARV:b\nUCE\n",
},
{
name: "literal --",
cmdLine: []string{"/urunit", "/app", "a", "--", "b", "c"},
expected: "ARC:5\nARV:/app\nARV:a\nARV:--\nARV:b\nARV:c\nUCE\n",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
l := &Linux{InitrdConf: true, RootFsType: "virtiofs"}
require.NoError(t, l.parseCmdLine(tc.cmdLine))
assert.Contains(t, l.buildUrunitConfig(), tc.expected)

cmd, err := l.CommandString()
require.NoError(t, err)
assert.True(t, strings.HasSuffix(cmd, " init=/urunit"), "unexpected command line: %s", cmd)
})
}
}

// TestLinuxUrunitLongArgs covers an argument list longer than the kernel
// command line (2048 bytes) with more than 32 arguments.
func TestLinuxUrunitLongArgs(t *testing.T) {
t.Parallel()

cmdLine := []string{"/urunit", "/app"}
for i := range 40 {
cmdLine = append(cmdLine, strconv.Itoa(i)+strings.Repeat("x", 100))
}
l := &Linux{InitrdConf: true, RootFsType: "virtiofs"}
require.NoError(t, l.parseCmdLine(cmdLine))
conf := l.buildUrunitConfig()

assert.Contains(t, conf, "\nARC:41\n")
assert.Equal(t, 41, strings.Count(conf, "\nARV:"))
assert.Contains(t, conf, "\nARV:"+cmdLine[41]+"\nUCE\n")

cmd, err := l.CommandString()
require.NoError(t, err)
assert.True(t, strings.HasSuffix(cmd, " init=/urunit"), "unexpected command line: %s", cmd)
}