Skip to content

Commit ab10136

Browse files
committed
refactor(rumprun): Refactor json construction
Rumprun accepts a json with various configuration options (e.g. net/block) when it boots. However, it does not follow the correct json syntax. Therefore, we can not use a typical marshal function to create the respective json. This patch creates a "json" string as Rumprun expects it and can read it. Furthermore, it adds support for passing environment variables in rumprun and handles properly the cases where block or network is not used for the unikernel. Signed-off-by: Charalampos Mainas <cmainas@nubificus.co.uk>
1 parent 6d4cf5d commit ab10136

File tree

2 files changed

+108
-31
lines changed

2 files changed

+108
-31
lines changed

pkg/unikontainers/unikernels/rumprun.go

Lines changed: 87 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,18 @@ const RumprunUnikernel string = "rumprun"
2424
const SubnetMask125 = "128.0.0.0"
2525

2626
type Rumprun struct {
27-
Command string `json:"cmdline"`
28-
Net RumprunNet `json:"net"`
29-
Blk RumprunBlk `json:"blk"`
27+
Command string
28+
Envs []string
29+
Net RumprunNet
30+
Blk RumprunBlk
3031
}
3132

32-
type RumprunNoNet struct {
33-
Command string `json:"cmdline"`
34-
Blk RumprunBlk `json:"blk"`
33+
type RumprunCmd struct {
34+
CmdLine string `json:"cmdline"`
3535
}
3636

37-
type RumprunCmd struct {
38-
Cmdline string `json:"cmdline"`
37+
type RumprunEnv struct {
38+
Env string `json:"env"`
3939
}
4040

4141
type RumprunNet struct {
@@ -56,25 +56,69 @@ type RumprunBlk struct {
5656
}
5757

5858
func (r *Rumprun) CommandString() (string, error) {
59-
// if EthDeviceMask is empty, there is no network support. omit every relevant field
60-
if r.Net.Mask == "" {
61-
tmp := RumprunNoNet{
62-
Command: r.Command,
63-
Blk: r.Blk,
59+
// Rumprun accepts a JSON string to configure the unikernel. However,
60+
// Rumprun does not use a valid JSON format. Therefore, we manually
61+
// construct the JSON instead of using Go's json Marshal.
62+
// For more information check https://github.com/rumpkernel/rumprun/blob/master/doc/config.md
63+
cmdJSONString := ""
64+
envJSONString := ""
65+
netJSONString := ""
66+
blkJSONString := ""
67+
cmd := RumprunCmd{
68+
CmdLine: r.Command,
69+
}
70+
cmdJSON, err := json.Marshal(cmd)
71+
if err != nil {
72+
return "", fmt.Errorf("Could not Marshal cmdline: %v", err)
73+
}
74+
cmdJSONString = string(cmdJSON)
75+
for i, eVar := range r.Envs {
76+
eVar := RumprunEnv{
77+
Env: eVar,
6478
}
65-
jsonData, err := json.Marshal(tmp)
79+
oneVarJSON, err := json.Marshal(eVar)
80+
if err != nil {
81+
return "", fmt.Errorf("Could not Marshal environment variable: %v", err)
82+
}
83+
if i != 0 {
84+
envJSONString += ","
85+
}
86+
oneVarJSONString := string(oneVarJSON)
87+
oneVarJSONString = strings.TrimPrefix(oneVarJSONString, "{")
88+
oneVarJSONString = strings.TrimSuffix(oneVarJSONString, "}")
89+
envJSONString += oneVarJSONString
90+
}
91+
// if Address is empty, we will spawn the unikernel without networking
92+
if r.Net.Address != "" {
93+
netJSON, err := json.Marshal(r.Net)
6694
if err != nil {
6795
return "", err
6896
}
69-
jsonStr := string(jsonData)
70-
return jsonStr, nil
97+
netJSONString = "\"net\":"
98+
netJSONString += string(netJSON)
7199
}
72-
jsonData, err := json.Marshal(r)
73-
if err != nil {
74-
return "", err
100+
// if Source is empty, we will spawn the unikernel without a block device
101+
if r.Blk.Source != "" {
102+
blkJSON, err := json.Marshal(r.Blk)
103+
if err != nil {
104+
return "", err
105+
}
106+
blkJSONString = "\"blk\":"
107+
blkJSONString += string(blkJSON)
75108
}
76-
jsonStr := string(jsonData)
77-
return jsonStr, nil
109+
finalJSONString := strings.TrimSuffix(cmdJSONString, "}")
110+
if envJSONString != "" {
111+
finalJSONString += "," + envJSONString
112+
}
113+
if netJSONString != "" {
114+
finalJSONString += "," + netJSONString
115+
}
116+
if blkJSONString != "" {
117+
finalJSONString += "," + blkJSONString
118+
}
119+
finalJSONString += "}"
120+
fmt.Println(finalJSONString)
121+
return finalJSONString, nil
78122
}
79123

80124
func (r *Rumprun) SupportsBlock() bool {
@@ -117,9 +161,13 @@ func (r *Rumprun) MonitorCli(_ string) string {
117161
func (r *Rumprun) Init(data UnikernelParams) error {
118162
// if EthDeviceMask is empty, there is no network support
119163
if data.EthDeviceMask != "" {
120-
// FIXME: in the case of rumprun & k8s, we need to explicitly set the mask
121-
// to an inclusive value (eg 1 or 0), as NetBSD complains and does not set the default gw
122-
// if it is not reachable from the IP address directly.
164+
// FIXME: in the case of rumprun & k8s, we need to identofy
165+
// the reason that networking is not working properly.
166+
// One reason could be that the gw is in different subnet
167+
// than the IP of the unikernel.
168+
// For that reason, we might need to set the mask to an
169+
// inclusive value (e.g. 0 or 1).
170+
// However, further exploration of this issue is necessary.
123171
mask, err := subnetMaskToCIDR(SubnetMask125)
124172
if err != nil {
125173
return err
@@ -131,14 +179,25 @@ func (r *Rumprun) Init(data UnikernelParams) error {
131179
r.Net.Address = data.EthDeviceIP
132180
r.Net.Mask = fmt.Sprintf("%d", mask)
133181
r.Net.Gateway = data.EthDeviceGateway
182+
} else {
183+
// Set address to empty string so we can know that no network
184+
// was specified.
185+
r.Net.Address = ""
134186
}
135187

136-
r.Blk.Source = "etfs"
137-
r.Blk.Path = "/dev/ld0a"
138-
r.Blk.FsType = "blk"
139-
r.Blk.Mountpoint = "/data"
188+
if data.BlockMntPoint != "" {
189+
r.Blk.Source = "etfs"
190+
r.Blk.Path = "/dev/ld0a"
191+
r.Blk.FsType = "blk"
192+
r.Blk.Mountpoint = data.BlockMntPoint
193+
} else {
194+
// Set source to empty string so we can know that no block
195+
// was specified.
196+
r.Blk.Source = ""
197+
}
140198

141199
r.Command = strings.Join(data.CmdLine, " ")
200+
r.Envs = data.EnvVars
142201

143202
return nil
144203
}

pkg/unikontainers/unikontainers.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,9 +204,10 @@ func (u *Unikontainer) Exec() error {
204204

205205
// populate unikernel params
206206
unikernelParams := unikernels.UnikernelParams{
207-
CmdLine: u.Spec.Process.Args,
208-
EnvVars: u.Spec.Process.Env,
209-
Version: unikernelVersion,
207+
CmdLine: u.Spec.Process.Args,
208+
EnvVars: u.Spec.Process.Env,
209+
Version: unikernelVersion,
210+
BlockMntPoint: "",
210211
}
211212
if len(unikernelParams.CmdLine) == 0 {
212213
unikernelParams.CmdLine = strings.Fields(u.State.Annotations[annotCmdLine])
@@ -287,6 +288,14 @@ func (u *Unikontainer) Exec() error {
287288
if u.State.Annotations[annotBlock] != "" && unikernel.SupportsBlock() {
288289
vmmArgs.BlockDevice = u.State.Annotations[annotBlock]
289290
unikernelParams.RootFSType = "block"
291+
if u.State.Annotations[annotBlockMntPoint] != "" {
292+
unikernelParams.BlockMntPoint = u.State.Annotations[annotBlockMntPoint]
293+
} else {
294+
// NOTE: If the user has not specified the
295+
// mount point for the block device, then we will
296+
// use /data as a default mount point.
297+
unikernelParams.RootFSType = "/data"
298+
}
290299
if withRootfsMount {
291300
uniklog.Warnf("Setting both Block and MountRootfs annotations is not supported yet. Only block will be used.")
292301
withRootfsMount = false
@@ -328,6 +337,15 @@ func (u *Unikontainer) Exec() error {
328337
}
329338
vmmArgs.BlockDevice = rootFsDevice.Device
330339
unikernelParams.RootFSType = "block"
340+
// NOTE: Rumprun does not allow us to mount
341+
// anything at '/'. As a result, we use the
342+
// /data mount point for Rumprun. For all the
343+
// other guests we use '/'.
344+
if unikernelType == "rumprun" {
345+
unikernelParams.BlockMntPoint = "/data"
346+
} else {
347+
unikernelParams.BlockMntPoint = "/"
348+
}
331349
dmPath = rootFsDevice.Device
332350
}
333351
}

0 commit comments

Comments
 (0)