Skip to content

Commit

Permalink
global: split Akvorado into 3 services
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentbernat committed Apr 1, 2022
1 parent a336370 commit 1dc2537
Show file tree
Hide file tree
Showing 179 changed files with 1,768 additions and 1,263 deletions.
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/bin/
/test/
/flow/decoder/flow*.pb.go
/inlet/flow/decoder/flow*.pb.go

/web/data/node_modules/
/web/data/assets/generated/
/console/data/node_modules/
/console/data/assets/generated/
18 changes: 9 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ M = $(shell if [ "$$(tput colors 2> /dev/null || echo 0)" -ge 8 ]; then printf "

export GO111MODULE=on

GENERATED = flow/decoder/flow-1.pb.go web/data/node_modules web/data/assets/generated
GENERATED = inlet/flow/decoder/flow-1.pb.go console/data/node_modules console/data/assets/generated

.PHONY: all
all: fmt lint $(GENERATED) | $(BIN) ; $(info $(M) building executable…) @ ## Build program binary
Expand Down Expand Up @@ -47,17 +47,17 @@ $(BIN)/protoc-gen-go: PACKAGE=google.golang.org/protobuf/cmd/protoc-gen-go

# Generated files

flow/decoder/%.pb.go: flow/data/schemas/%.proto | $(PROTOC_GEN_GO) ; $(info $(M) compiling protocol buffers definition…)
inlet/flow/decoder/%.pb.go: inlet/flow/data/schemas/%.proto | $(PROTOC_GEN_GO) ; $(info $(M) compiling protocol buffers definition…)
$Q $(PROTOC) -I=. --plugin=$(PROTOC_GEN_GO) --go_out=. --go_opt=module=$(MODULE) $<

web/data/node_modules: web/data/package.json web/data/yarn.lock ; $(info $(M) fetching node modules…)
$Q yarn install --frozen-lockfile --cwd web/data && touch $@
web/data/assets/generated: web/data/node_modules Makefile ; $(info $(M) copying static assets…)
console/data/node_modules: console/data/package.json console/data/yarn.lock ; $(info $(M) fetching node modules…)
$Q yarn install --frozen-lockfile --cwd console/data && touch $@
console/data/assets/generated: console/data/node_modules Makefile ; $(info $(M) copying static assets…)
$Q rm -rf $@ && mkdir -p $@/stylesheets $@/javascript $@/fonts
$Q cp web/data/node_modules/@mdi/font/fonts/materialdesignicons-webfont.woff* $@/fonts/.
$Q cp web/data/node_modules/@mdi/font/css/materialdesignicons.min.css $@/stylesheets/.
$Q cp web/data/node_modules/bootstrap/dist/css/bootstrap.min.css $@/stylesheets/.
$Q cp web/data/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js $@/javascript/.
$Q cp console/data/node_modules/@mdi/font/fonts/materialdesignicons-webfont.woff* $@/fonts/.
$Q cp console/data/node_modules/@mdi/font/css/materialdesignicons.min.css $@/stylesheets/.
$Q cp console/data/node_modules/bootstrap/dist/css/bootstrap.min.css $@/stylesheets/.
$Q cp console/data/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js $@/javascript/.

# These files are versioned in Git, but we may want to update them.
clickhouse/data/protocols.csv:
Expand Down
48 changes: 48 additions & 0 deletions cmd/components.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package cmd

import (
"fmt"

"akvorado/common/daemon"
"akvorado/common/reporter"
)

// StartStopComponents activate/deactivate components in order.
func StartStopComponents(r *reporter.Reporter, daemonComponent daemon.Component, otherComponents []interface{}) error {
components := append([]interface{}{r, daemonComponent}, otherComponents...)
startedComponents := []interface{}{}
defer func() {
for _, cmp := range startedComponents {
if stopperC, ok := cmp.(stopper); ok {
if err := stopperC.Stop(); err != nil {
r.Err(err).Msg("unable to stop component, ignoring")
}
}
}
}()
for _, cmp := range components {
if starterC, ok := cmp.(starter); ok {
if err := starterC.Start(); err != nil {
return fmt.Errorf("unable to start component: %w", err)
}
}
startedComponents = append([]interface{}{cmp}, startedComponents...)
}

r.Info().
Str("version", Version).Str("build-date", BuildDate).
Msg("akvorado has started")

select {
case <-daemonComponent.Terminated():
r.Info().Msg("stopping all components")
}
return nil
}

type starter interface {
Start() error
}
type stopper interface {
Stop() error
}
81 changes: 81 additions & 0 deletions cmd/components_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package cmd_test

import (
"errors"
"testing"

"akvorado/cmd"
"akvorado/common/daemon"
"akvorado/common/helpers"
"akvorado/common/reporter"
)

type Startable struct {
Started bool
}
type Stopable struct {
Stopped bool
}

func (c *Startable) Start() error {
c.Started = true
return nil
}
func (c *Stopable) Stop() error {
c.Stopped = true
return nil
}

type ComponentStartStop struct {
Startable
Stopable
}
type ComponentStop struct {
Stopable
}
type ComponentStart struct {
Startable
}
type ComponentNone struct{}
type ComponentStartError struct {
Stopable
}

func (c ComponentStartError) Start() error {
return errors.New("nooo")
}

func TestStartStop(t *testing.T) {
r := reporter.NewMock(t)
daemonComponent := daemon.NewMock(t)
otherComponents := []interface{}{
&ComponentStartStop{},
&ComponentStop{},
&ComponentStart{},
&ComponentNone{},
&ComponentStartError{},
&ComponentStartStop{},
}
if err := cmd.StartStopComponents(r, daemonComponent, otherComponents); err == nil {
t.Error("StartStopComponents() did not trigger an error")
}

expected := []interface{}{
&ComponentStartStop{
Startable: Startable{Started: true},
Stopable: Stopable{Stopped: true},
},
&ComponentStop{
Stopable: Stopable{Stopped: true},
},
&ComponentStart{
Startable: Startable{Started: true},
},
&ComponentNone{},
&ComponentStartError{},
&ComponentStartStop{},
}
if diff := helpers.Diff(otherComponents, expected); diff != "" {
t.Errorf("StartStopComponents() (-got, +want):\n%s", diff)
}
}
107 changes: 107 additions & 0 deletions cmd/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package cmd

import (
"fmt"
"io"
"io/ioutil"
"os"
"strconv"
"strings"

"github.com/mitchellh/mapstructure"
"gopkg.in/yaml.v2"

"akvorado/inlet/flow"
)

// ConfigRelatedOptions are command-line options related to handling a
// configuration file.
type ConfigRelatedOptions struct {
Path string
Dump bool
}

// Parse parses the configuration file (if present) and the
// environment variables into the provided configuration.
func (c ConfigRelatedOptions) Parse(out io.Writer, component string, config interface{}) error {
var rawConfig map[string]interface{}
if cfgFile := c.Path; cfgFile != "" {
input, err := ioutil.ReadFile(cfgFile)
if err != nil {
return fmt.Errorf("unable to read configuration file: %w", err)
}
if err := yaml.Unmarshal(input, &rawConfig); err != nil {
return fmt.Errorf("unable to parse configuration file: %w", err)
}
}

// Parse provided configuration
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Result: &config,
ErrorUnused: true,
Metadata: nil,
WeaklyTypedInput: true,
MatchName: func(mapKey, fieldName string) bool {
key := strings.ToLower(strings.ReplaceAll(mapKey, "-", ""))
field := strings.ToLower(fieldName)
return key == field
},
DecodeHook: mapstructure.ComposeDecodeHookFunc(
flow.ConfigurationUnmarshalerHook(),
mapstructure.TextUnmarshallerHookFunc(),
mapstructure.StringToTimeDurationHookFunc(),
mapstructure.StringToSliceHookFunc(","),
),
})
if err != nil {
return fmt.Errorf("unable to create configuration decoder: %w", err)
}
if err := decoder.Decode(rawConfig); err != nil {
return fmt.Errorf("unable to parse configuration: %w", err)
}

// Override with environment variables
for _, keyval := range os.Environ() {
kv := strings.SplitN(keyval, "=", 2)
if len(kv) != 2 {
continue
}
kk := strings.Split(kv[0], "_")
if len(kk) < 3 || kk[0] != "AKVORADO" || kk[1] != strings.ToUpper(component) {
continue
}
// From AKVORADO_CMP_SQUID_PURPLE_QUIRK=47, we
// build a map "squid -> purple -> quirk ->
// 47". From AKVORADO_CMP_SQUID_3_PURPLE=47, we
// build "squid[3] -> purple -> 47"
var rawConfig interface{}
rawConfig = kv[1]
for i := len(kk) - 1; i > 1; i-- {
if index, err := strconv.Atoi(kk[i]); err == nil {
newRawConfig := make([]interface{}, index+1)
newRawConfig[index] = rawConfig
rawConfig = newRawConfig
} else {
rawConfig = map[string]interface{}{
kk[i]: rawConfig,
}
}
}
if err := decoder.Decode(rawConfig); err != nil {
return fmt.Errorf("unable to parse override %q: %w", kv[0], err)
}
}

// Dump configuration if requested
if c.Dump {
output, err := yaml.Marshal(config)
if err != nil {
return fmt.Errorf("unable to dump configuration: %w", err)
}
out.Write([]byte("---\n"))
out.Write(output)
out.Write([]byte("\n"))
}

return nil
}
Loading

0 comments on commit 1dc2537

Please sign in to comment.