mirror of
https://github.com/akvorado/akvorado.git
synced 2025-12-11 22:14:02 +01:00
132 lines
3.0 KiB
Go
132 lines
3.0 KiB
Go
// SPDX-FileCopyrightText: 2022 Free Mobile
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
// Package flow handle incoming NetFlow/IPFIX/sflow flows.
|
|
package flow
|
|
|
|
import (
|
|
"errors"
|
|
"sync"
|
|
|
|
"gopkg.in/tomb.v2"
|
|
|
|
"akvorado/common/daemon"
|
|
"akvorado/common/helpers"
|
|
"akvorado/common/httpserver"
|
|
"akvorado/common/pb"
|
|
"akvorado/common/reporter"
|
|
"akvorado/inlet/flow/input"
|
|
"akvorado/inlet/kafka"
|
|
)
|
|
|
|
// Component represents the flow component.
|
|
type Component struct {
|
|
r *reporter.Reporter
|
|
d *Dependencies
|
|
t tomb.Tomb
|
|
config Configuration
|
|
|
|
inputs []input.Input
|
|
payloadPool sync.Pool
|
|
}
|
|
|
|
// Dependencies are the dependencies of the flow component.
|
|
type Dependencies struct {
|
|
Daemon daemon.Component
|
|
HTTP *httpserver.Component
|
|
Kafka *kafka.Component
|
|
}
|
|
|
|
// New creates a new flow component.
|
|
func New(r *reporter.Reporter, configuration Configuration, dependencies Dependencies) (*Component, error) {
|
|
if len(configuration.Inputs) == 0 {
|
|
return nil, errors.New("no input configured")
|
|
}
|
|
|
|
c := Component{
|
|
r: r,
|
|
d: &dependencies,
|
|
config: configuration,
|
|
inputs: make([]input.Input, len(configuration.Inputs)),
|
|
payloadPool: sync.Pool{
|
|
New: func() any {
|
|
minPayload := 2000
|
|
if helpers.Testing() {
|
|
// Ensure we test the extension case.
|
|
minPayload = 5
|
|
}
|
|
s := make([]byte, minPayload)
|
|
return &s
|
|
},
|
|
},
|
|
}
|
|
|
|
// Initialize inputs
|
|
for idx, input := range c.config.Inputs {
|
|
var err error
|
|
c.inputs[idx], err = input.Config.New(r, c.d.Daemon, c.Send(input))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
c.d.Daemon.Track(&c.t, "inlet/flow")
|
|
|
|
return &c, nil
|
|
}
|
|
|
|
// Send sends a raw flow to Kafka.
|
|
func (c *Component) Send(config InputConfiguration) input.SendFunc {
|
|
return func(exporter string, flow *pb.RawFlow) {
|
|
flow.TimestampSource = config.TimestampSource
|
|
flow.Decoder = config.Decoder
|
|
flow.UseSourceAddress = config.UseSrcAddrForExporterAddr
|
|
|
|
// Get a payload from the pool and extend it if needed. We use a pool of
|
|
// pointers to slice as we may have to extend the capacity of the slice.
|
|
// We keep the original pointer to avoid an extra allocation on the heap
|
|
// when returning the slice to the pool.
|
|
ptr := c.payloadPool.Get().(*[]byte)
|
|
bytes := *ptr
|
|
n := flow.SizeVT()
|
|
if cap(bytes) < n {
|
|
bytes = make([]byte, n+100)
|
|
*ptr = bytes
|
|
}
|
|
|
|
// Marshal to it, send it to Kafka and return it when done
|
|
if n, err := flow.MarshalToSizedBufferVT(bytes[:n]); err == nil {
|
|
c.d.Kafka.Send(exporter, bytes[:n], func() {
|
|
c.payloadPool.Put(ptr)
|
|
})
|
|
} else {
|
|
c.payloadPool.Put(ptr)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Start starts the flow component.
|
|
func (c *Component) Start() error {
|
|
for _, input := range c.inputs {
|
|
err := input.Start()
|
|
stopper := input.Stop
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.t.Go(func() error {
|
|
<-c.t.Dying()
|
|
stopper()
|
|
return nil
|
|
})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Stop stops the flow component
|
|
func (c *Component) Stop() error {
|
|
defer c.r.Info().Msg("flow component stopped")
|
|
c.r.Info().Msg("stopping flow component")
|
|
c.t.Kill(nil)
|
|
return c.t.Wait()
|
|
}
|