@@ -19,14 +19,28 @@ import (
19
19
"errors"
20
20
"fmt"
21
21
"os"
22
+ "os/exec"
22
23
"strconv"
23
24
"strings"
24
25
25
26
"github.com/firecracker-microvm/firecracker-go-sdk"
27
+ "github.com/hashicorp/go-multierror"
26
28
"github.com/jessevdk/go-flags"
27
29
log "github.com/sirupsen/logrus"
28
30
)
29
31
32
+ const (
33
+ terminalProgram = "xterm"
34
+ // consoleXterm indicates that the machine's console should be presented in an xterm
35
+ consoleXterm = "xterm"
36
+ // consoleStdio indicates that the machine's console should re-use the parent's stdio streams
37
+ consoleStdio = "stdio"
38
+ // consoleFile inddicates that the machine's console should be presented in files rather than stdout/stderr
39
+ consoleFile = "file"
40
+ // consoleNone indicates that the machine's console IO should be discarded
41
+ consoleNone = "none"
42
+ )
43
+
30
44
func parseBlockDevices (entries []string ) ([]firecracker.BlockDevice , error ) {
31
45
var devices []firecracker.BlockDevice
32
46
for _ , entry := range entries {
@@ -89,29 +103,33 @@ func parseVsocks(devices []string) ([]firecracker.VsockDevice, error) {
89
103
return result , nil
90
104
}
91
105
106
+ type options struct {
107
+ FcBinary string `long:"firecracker-binary" description:"Path to firecracker binary"`
108
+ FcConsole string `long:"firecracker-console" description:"Console type (stdio|file|xterm|none)" default:"stdio"`
109
+ FcKernelImage string `long:"kernel" description:"Path to the kernel image" default:"./vmlinux"`
110
+ FcKernelCmdLine string `long:"kernel-opts" description:"Kernel commandline" default:"ro console=ttyS0 noapic reboot=k panic=1 pci=off nomodules"`
111
+ FcRootDrivePath string `long:"root-drive" description:"Path to root disk image"`
112
+ FcRootPartUUID string `long:"root-partition" description:"Root partition UUID"`
113
+ FcAdditionalDrives []string `long:"add-drive" description:"Path to additional drive, suffixed with :ro or :rw, can be specified multiple times"`
114
+ FcNicConfig string `long:"tap-device" description:"NIC info, specified as DEVICE/MAC"`
115
+ FcVsockDevices []string `long:"vsock-device" description:"Vsock interface, specified as PATH:CID. Multiple OK"`
116
+ FcLogFifo string `long:"vmm-log-fifo" description:"FIFO for firecracker logs"`
117
+ FcLogLevel string `long:"log-level" description:"vmm log level" default:"Debug"`
118
+ FcLogStdoutFilePath string `long:"log-stdout-file" description:"Specifies where stdout logs should be placed"`
119
+ FcLogStderrFilePath string `long:"log-stderr-file" description:"Specifies where stderr logs should be placed"`
120
+ FcMetricsFifo string `long:"metrics-fifo" description:"FIFO for firecracker metrics"`
121
+ FcDisableHt bool `long:"disable-hyperthreading" short:"t" description:"Disable CPU Hyperthreading"`
122
+ FcCPUCount int64 `long:"ncpus" short:"c" description:"Number of CPUs" default:"1"`
123
+ FcCPUTemplate string `long:"cpu-template" description:"Firecracker CPU Template (C3 or T2)"`
124
+ FcMemSz int64 `long:"memory" short:"m" description:"VM memory, in MiB" default:"512"`
125
+ FcMetadata string `long:"metadata" description:"Firecracker Meatadata for MMDS (json)"`
126
+ Debug bool `long:"debug" short:"d" description:"Enable debug output"`
127
+ Help bool `long:"help" short:"h" description:"Show usage"`
128
+ }
129
+
92
130
func main () {
93
131
var err error
94
- var opts struct {
95
- FcBinary string `long:"firecracker-binary" description:"Path to firecracker binary"`
96
- FcConsole string `long:"firecracker-console" description:"Console type (stdio|xterm|none)" default:"stdio"`
97
- FcKernelImage string `long:"kernel" description:"Path to the kernel image" default:"./vmlinux"`
98
- FcKernelCmdLine string `long:"kernel-opts" description:"Kernel commandline" default:"ro console=ttyS0 noapic reboot=k panic=1 pci=off nomodules"`
99
- FcRootDrivePath string `long:"root-drive" description:"Path to root disk image"`
100
- FcRootPartUUID string `long:"root-partition" description:"Root partition UUID"`
101
- FcAdditionalDrives []string `long:"add-drive" description:"Path to additional drive, suffixed with :ro or :rw, can be specified multiple times"`
102
- FcNicConfig string `long:"tap-device" description:"NIC info, specified as DEVICE/MAC"`
103
- FcVsockDevices []string `long:"vsock-device" description:"Vsock interface, specified as PATH:CID. Multiple OK"`
104
- FcLogFifo string `long:"vmm-log-fifo" description:"FIFO for firecracker logs"`
105
- FcLogLevel string `long:"log-level" description:"vmm log level" default:"Debug"`
106
- FcMetricsFifo string `long:"metrics-fifo" description:"FIFO for firecracker metrics"`
107
- FcDisableHt bool `long:"disable-hyperthreading" short:"t" description:"Disable CPU Hyperthreading"`
108
- FcCPUCount int64 `long:"ncpus" short:"c" description:"Number of CPUs" default:"1"`
109
- FcCPUTemplate string `long:"cpu-template" description:"Firecracker CPU Template (C3 or T2)"`
110
- FcMemSz int64 `long:"memory" short:"m" description:"VM memory, in MiB" default:"512"`
111
- FcMetadata string `long:"metadata" description:"Firecracker Meatadata for MMDS (json)"`
112
- Debug bool `long:"debug" short:"d" description:"Enable debug output"`
113
- Help bool `long:"help" short:"h" description:"Show usage"`
114
- }
132
+ opts := options {}
115
133
116
134
p := flags .NewParser (& opts , 0 )
117
135
_ , err = p .Parse ()
@@ -172,7 +190,6 @@ func main() {
172
190
}
173
191
174
192
fcCfg := firecracker.Config {
175
- BinPath : opts .FcBinary ,
176
193
SocketPath : "./firecracker.sock" ,
177
194
LogFifo : opts .FcLogFifo ,
178
195
LogLevel : opts .FcLogLevel ,
@@ -192,15 +209,30 @@ func main() {
192
209
Debug : opts .Debug ,
193
210
}
194
211
195
- m , err := firecracker . NewMachine ( fcCfg , firecracker . WithLogger ( log . NewEntry ( logger )))
196
- if err != nil {
197
- log . Fatalf ( "Failed creating machine: %s" , err )
212
+ if len ( os . Args ) == 1 {
213
+ p . WriteHelp ( os . Stderr )
214
+ os . Exit ( 0 )
198
215
}
199
216
200
217
ctx := context .Background ()
201
218
vmmCtx , vmmCancel := context .WithCancel (ctx )
202
219
defer vmmCancel ()
203
- errchan , err := m .Init (ctx )
220
+
221
+ cmd , cleanup , err := buildCommand (vmmCtx , fcCfg .SocketPath , opts )
222
+ if err != nil {
223
+ log .Fatalf ("Failed to build command: %v" , err )
224
+ }
225
+ defer cleanup ()
226
+
227
+ m , err := firecracker .NewMachine (fcCfg ,
228
+ firecracker .WithLogger (log .NewEntry (logger )),
229
+ firecracker .WithProcessRunner (cmd ),
230
+ )
231
+ if err != nil {
232
+ log .Fatalf ("Failed creating machine: %s" , err )
233
+ }
234
+
235
+ errCh , err := m .Init (vmmCtx )
204
236
if err != nil {
205
237
log .Fatalf ("Firecracker Init returned error %s" , err )
206
238
}
@@ -211,16 +243,76 @@ func main() {
211
243
log .Fatalf ("Firecracker SetMetadata returned error %s" , err )
212
244
}
213
245
}
214
- err = m . StartInstance ( vmmCtx )
215
- if err != nil {
216
- log .Fatalf ("Firecracker StartInstance returned error %s " , err )
246
+
247
+ if err := m . StartInstance ( vmmCtx ); err != nil {
248
+ log .Fatalf ("Failed to start instance: %v " , err )
217
249
}
218
250
219
251
// wait for the VMM to exit
220
- err = <- errchan
221
- if err != nil {
252
+ if err := <- errCh ; err != nil {
222
253
log .Fatalf ("startVMM returned error %s" , err )
223
- } else {
224
- log .Printf ("startVMM was happy" )
225
254
}
255
+ log .Printf ("startVMM was happy" )
256
+ }
257
+
258
+ func buildCommand (ctx context.Context , socketPath string , opts options ) (* exec.Cmd , func () error , error ) {
259
+ var cmd * exec.Cmd
260
+ b := firecracker.VMCommandBuilder {}
261
+
262
+ fn := func () error {
263
+ return nil
264
+ }
265
+
266
+ switch opts .FcConsole {
267
+ case consoleXterm :
268
+ cmd = b .WithBin (terminalProgram ).
269
+ AddArgs ("-e" , opts .FcBinary , "--api-sock" , socketPath ).
270
+ Build (ctx )
271
+ case consoleStdio :
272
+ cmd = b .WithBin (opts .FcBinary ).
273
+ WithSocketPath (socketPath ).
274
+ WithStdin (os .Stdin ).
275
+ WithStdout (os .Stdout ).
276
+ WithStderr (os .Stderr ).
277
+ Build (ctx )
278
+ case consoleFile :
279
+ stdout , err := generateLogFile (opts .FcLogStdoutFilePath )
280
+ if err != nil {
281
+ return nil , nil , err
282
+ }
283
+
284
+ stderr , err := generateLogFile (opts .FcLogStderrFilePath )
285
+ if err != nil {
286
+ return nil , nil , err
287
+ }
288
+
289
+ fn = func () error {
290
+ var merr * multierror.Error
291
+ if err := stdout .Close (); err != nil {
292
+ merr = multierror .Append (merr , err )
293
+ }
294
+
295
+ if err := stderr .Close (); err != nil {
296
+ merr = multierror .Append (merr , err )
297
+ }
298
+
299
+ return merr .ErrorOrNil ()
300
+ }
301
+
302
+ cmd = b .WithBin (opts .FcBinary ).
303
+ WithSocketPath (socketPath ).
304
+ WithStdout (stdout ).
305
+ WithStderr (stderr ).
306
+ Build (ctx )
307
+ default :
308
+ cmd = b .WithBin (opts .FcBinary ).
309
+ WithSocketPath (socketPath ).
310
+ Build (ctx )
311
+ }
312
+
313
+ return cmd , fn , nil
314
+ }
315
+
316
+ func generateLogFile (filename string ) (* os.File , error ) {
317
+ return os .OpenFile (filename , os .O_CREATE | os .O_APPEND | os .O_WRONLY , 0644 )
226
318
}
0 commit comments