Skip to content

Commit 2cb5e89

Browse files
committed
Support Firecracker's new logging and metrics
Previously Firecracker had a single operation to set logging and metrics, and the fields specifically mentioned "FIFO". Now the operation is broken into two and it doesn't mention "FIFO" anymore. This change keeps MetricsFifo and LogFifo to be backward-compatible. Signed-off-by: Kazuyoshi Kato <[email protected]> fix
1 parent ff05ab7 commit 2cb5e89

File tree

4 files changed

+147
-38
lines changed

4 files changed

+147
-38
lines changed

firecracker.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,25 @@ func (f *Client) PutLogger(ctx context.Context, logger *models.Logger, opts ...P
9494
return f.client.Operations.PutLogger(loggerParams)
9595
}
9696

97+
// PutMetricsOpt is a functional option to be used for the PutMetrics API in
98+
// setting any additional optional fields.
99+
type PutMetricsOpt func(*ops.PutMetricsParams)
100+
101+
// PutMetrics is a wrapper for the swagger generated client to make calling of
102+
// the API easier.
103+
func (f *Client) PutMetrics(ctx context.Context, metrics *models.Metrics, opts ...PutMetricsOpt) (*ops.PutMetricsNoContent, error) {
104+
timeout, cancel := context.WithTimeout(ctx, time.Duration(f.firecrackerRequestTimeout)*time.Millisecond)
105+
defer cancel()
106+
107+
params := ops.NewPutMetricsParamsWithContext(timeout)
108+
params.SetBody(metrics)
109+
for _, opt := range opts {
110+
opt(params)
111+
}
112+
113+
return f.client.Operations.PutMetrics(params)
114+
}
115+
97116
// PutMachineConfigurationOpt is a functional option to be used for the
98117
// PutMachineConfiguration API in setting any additional optional fields.
99118
type PutMachineConfigurationOpt func(*ops.PutMachineConfigurationParams)

handlers.go

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -119,40 +119,44 @@ var StartVMMHandler = Handler{
119119
},
120120
}
121121

122-
// CreateLogFilesHandler is a named handler that will create the fifo log files
123-
var CreateLogFilesHandler = Handler{
124-
Name: CreateLogFilesHandlerName,
125-
Fn: func(ctx context.Context, m *Machine) error {
126-
logFifoPath := m.Cfg.LogFifo
127-
metricsFifoPath := m.Cfg.MetricsFifo
128-
129-
if len(logFifoPath) == 0 || len(metricsFifoPath) == 0 {
130-
// logging is disabled
131-
return nil
132-
}
133-
134-
if err := createFifos(logFifoPath, metricsFifoPath); err != nil {
135-
m.logger.Errorf("Unable to set up logging: %s", err)
122+
func createFifoOrFile(ctx context.Context, m *Machine, fifo, path string) error {
123+
if len(fifo) > 0 {
124+
if err := createFifo(fifo); err != nil {
136125
return err
137126
}
138127

139128
m.cleanupFuncs = append(m.cleanupFuncs,
140129
func() error {
141-
if err := os.Remove(logFifoPath); !os.IsNotExist(err) {
142-
return err
143-
}
144-
return nil
145-
},
146-
func() error {
147-
if err := os.Remove(metricsFifoPath); !os.IsNotExist(err) {
130+
if err := os.Remove(fifo); !os.IsNotExist(err) {
148131
return err
149132
}
150133
return nil
151134
},
152135
)
136+
} else if len(path) > 0 {
137+
file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
138+
if err != nil {
139+
return err
140+
}
141+
file.Close()
142+
}
143+
return nil
144+
}
145+
146+
// CreateLogFilesHandler is a named handler that will create the fifo log files
147+
var CreateLogFilesHandler = Handler{
148+
Name: CreateLogFilesHandlerName,
149+
Fn: func(ctx context.Context, m *Machine) error {
150+
if err := createFifoOrFile(ctx, m, m.Cfg.MetricsFifo, m.Cfg.MetricsPath); err != nil {
151+
return err
152+
}
153+
154+
if err := createFifoOrFile(ctx, m, m.Cfg.LogFifo, m.Cfg.LogPath); err != nil {
155+
return err
156+
}
153157

154158
if m.Cfg.FifoLogWriter != nil {
155-
if err := m.captureFifoToFile(ctx, m.logger, logFifoPath, m.Cfg.FifoLogWriter); err != nil {
159+
if err := m.captureFifoToFile(ctx, m.logger, m.Cfg.LogFifo, m.Cfg.FifoLogWriter); err != nil {
156160
m.logger.Warnf("captureFifoToFile() returned %s. Continuing anyway.", err)
157161
}
158162
}
@@ -171,6 +175,9 @@ var BootstrapLoggingHandler = Handler{
171175
if err := m.setupLogging(ctx); err != nil {
172176
return err
173177
}
178+
if err := m.setupMetrics(ctx); err != nil {
179+
return err
180+
}
174181
m.logger.Debugf("setup logging: success")
175182
return nil
176183
},

machine.go

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ type Config struct {
7878
// should be created.
7979
SocketPath string
8080

81+
// LogPath defines the file path where the Firecracker log is located.
82+
LogPath string
83+
8184
// LogFifo defines the file path where the Firecracker log named-pipe should
8285
// be located.
8386
LogFifo string
@@ -86,6 +89,10 @@ type Config struct {
8689
// "Error", "Warning", "Info", and "Debug", and are case-sensitive.
8790
LogLevel string
8891

92+
// MetricsPath defines the file path where the Firecracker metrics
93+
// is located.
94+
MetricsPath string
95+
8996
// MetricsFifo defines the file path where the Firecracker metrics
9097
// named-pipe should be located.
9198
MetricsFifo string
@@ -585,29 +592,29 @@ func (m *Machine) stopVMM() error {
585592
return nil
586593
}
587594

588-
// createFifos sets up the firecracker logging and metrics FIFOs
589-
func createFifos(logFifo, metricsFifo string) error {
590-
log.Debugf("Creating FIFO %s", logFifo)
591-
if err := syscall.Mkfifo(logFifo, 0700); err != nil {
595+
// createFifo sets up a FIFOs
596+
func createFifo(path string) error {
597+
log.Debugf("Creating FIFO %s", path)
598+
if err := syscall.Mkfifo(path, 0700); err != nil {
592599
return fmt.Errorf("Failed to create log fifo: %v", err)
593600
}
594-
595-
log.Debugf("Creating metric FIFO %s", metricsFifo)
596-
if err := syscall.Mkfifo(metricsFifo, 0700); err != nil {
597-
return fmt.Errorf("Failed to create metric fifo: %v", err)
598-
}
599601
return nil
600602
}
601603

602604
func (m *Machine) setupLogging(ctx context.Context) error {
603-
if len(m.Cfg.LogFifo) == 0 || len(m.Cfg.MetricsFifo) == 0 {
605+
path := m.Cfg.LogPath
606+
if len(m.Cfg.LogFifo) > 0 {
607+
path = m.Cfg.LogFifo
608+
}
609+
610+
if len(path) == 0 {
604611
// No logging configured
605-
m.logger.Printf("VMM logging and metrics disabled.")
612+
m.logger.Printf("VMM logging disabled.")
606613
return nil
607614
}
608615

609616
l := models.Logger{
610-
LogPath: String(m.Cfg.LogFifo),
617+
LogPath: String(path),
611618
Level: String(m.Cfg.LogLevel),
612619
ShowLevel: Bool(true),
613620
ShowLogOrigin: Bool(false),
@@ -618,10 +625,31 @@ func (m *Machine) setupLogging(ctx context.Context) error {
618625
return err
619626
}
620627

621-
m.logger.Debugf("Configured VMM logging to %s, metrics to %s",
622-
m.Cfg.LogFifo,
623-
m.Cfg.MetricsFifo,
624-
)
628+
m.logger.Debugf("Configured VMM logging to %s", path)
629+
630+
return nil
631+
}
632+
633+
func (m *Machine) setupMetrics(ctx context.Context) error {
634+
path := m.Cfg.MetricsPath
635+
if len(m.Cfg.MetricsFifo) > 0 {
636+
path = m.Cfg.MetricsFifo
637+
}
638+
639+
if len(path) == 0 {
640+
// No logging configured
641+
m.logger.Printf("VMM metrics disabled.")
642+
return nil
643+
}
644+
645+
_, err := m.client.PutMetrics(ctx, &models.Metrics{
646+
MetricsPath: String(path),
647+
})
648+
if err != nil {
649+
return err
650+
}
651+
652+
m.logger.Debugf("Configured VMM metrics to %s", path)
625653

626654
return nil
627655
}

machine_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,61 @@ func TestStartVMM(t *testing.T) {
419419
assert.False(t, closed)
420420
}
421421

422+
func TestLogAndMetrics(t *testing.T) {
423+
dir, err := ioutil.TempDir("", t.Name())
424+
require.NoError(t, err)
425+
defer os.RemoveAll(dir)
426+
427+
socketPath := filepath.Join(dir, "fc.sock")
428+
429+
cfg := Config{
430+
SocketPath: socketPath,
431+
DisableValidation: true,
432+
KernelImagePath: getVmlinuxPath(t),
433+
MachineCfg: models.MachineConfiguration{
434+
VcpuCount: Int64(1),
435+
MemSizeMib: Int64(64),
436+
CPUTemplate: models.CPUTemplate(models.CPUTemplateT2),
437+
HtEnabled: Bool(false),
438+
},
439+
MetricsPath: filepath.Join(dir, "fc-metrics.out"),
440+
LogPath: filepath.Join(dir, "fc.log"),
441+
LogLevel: "Debug",
442+
}
443+
ctx := context.Background()
444+
cmd := VMCommandBuilder{}.
445+
WithSocketPath(cfg.SocketPath).
446+
WithBin(getFirecrackerBinaryPath()).
447+
Build(ctx)
448+
m, err := NewMachine(ctx, cfg, WithProcessRunner(cmd), WithLogger(fctesting.NewLogEntry(t)))
449+
require.NoError(t, err)
450+
451+
timeout, cancel := context.WithTimeout(ctx, 250*time.Millisecond)
452+
defer cancel()
453+
454+
err = m.Start(timeout)
455+
require.NoError(t, err)
456+
defer m.StopVMM()
457+
458+
select {
459+
case <-timeout.Done():
460+
if timeout.Err() == context.DeadlineExceeded {
461+
t.Log("firecracker ran for 250ms")
462+
t.Run("TestStopVMM", func(t *testing.T) { testStopVMM(ctx, t, m) })
463+
} else {
464+
t.Errorf("startVMM returned %s", m.Wait(ctx))
465+
}
466+
}
467+
468+
metrics, err := os.Stat(cfg.MetricsPath)
469+
require.NoError(t, err)
470+
assert.NotEqual(t, 0, metrics.Size())
471+
472+
log, err := os.Stat(cfg.LogPath)
473+
require.NoError(t, err)
474+
assert.NotEqual(t, 0, log.Size())
475+
}
476+
422477
func TestStartVMMOnce(t *testing.T) {
423478
socketPath := filepath.Join("testdata", "TestStartVMMOnce.sock")
424479
defer os.Remove(socketPath)

0 commit comments

Comments
 (0)