Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refacto: decouple parser and listener #31

Merged
merged 2 commits into from
Jun 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions cmd/salt-exporter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import (

"github.com/kpetremann/salt-exporter/internal/logging"
"github.com/kpetremann/salt-exporter/internal/metrics"
"github.com/kpetremann/salt-exporter/pkg/events"
events "github.com/kpetremann/salt-exporter/pkg/event"
"github.com/kpetremann/salt-exporter/pkg/listener"
"github.com/kpetremann/salt-exporter/pkg/parser"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/rs/zerolog/log"
)
Expand Down Expand Up @@ -84,9 +86,10 @@ func main() {
eventChan := make(chan events.SaltEvent)

// listen and expose metric
eventListener := events.NewEventListener(ctx, eventChan)
parser := parser.NewEventParser(false)
eventListener := listener.NewEventListener(ctx, parser, eventChan)

go eventListener.ListenEvents(false)
go eventListener.ListenEvents()
go metrics.ExposeMetrics(ctx, eventChan, metricsConfig)

// start http server
Expand Down
11 changes: 7 additions & 4 deletions cmd/salt-live/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import (
"syscall"

"github.com/kpetremann/salt-exporter/internal/tui"
"github.com/kpetremann/salt-exporter/pkg/events"
"github.com/kpetremann/salt-exporter/pkg/event"
"github.com/kpetremann/salt-exporter/pkg/listener"
"github.com/kpetremann/salt-exporter/pkg/parser"
"github.com/rs/zerolog/log"

tea "github.com/charmbracelet/bubbletea"
Expand Down Expand Up @@ -47,9 +49,10 @@ func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()

eventChan := make(chan events.SaltEvent, *bufferSize)
eventListener := events.NewEventListener(ctx, eventChan)
go eventListener.ListenEvents(true)
eventChan := make(chan event.SaltEvent, *bufferSize)
parser := parser.NewEventParser(true)
eventListener := listener.NewEventListener(ctx, parser, eventChan)
go eventListener.ListenEvents()

p := tea.NewProgram(tui.NewModel(eventChan, *maxItems, *filter), tea.WithMouseCellMotion())
if _, err := p.Run(); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion internal/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"time"

"github.com/kpetremann/salt-exporter/internal/filters"
"github.com/kpetremann/salt-exporter/pkg/events"
events "github.com/kpetremann/salt-exporter/pkg/event"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/rs/zerolog/log"
Expand Down
4 changes: 2 additions & 2 deletions internal/tui/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ package tui
import (
"fmt"

"github.com/kpetremann/salt-exporter/pkg/events"
"github.com/kpetremann/salt-exporter/pkg/event"
)

type item struct {
title string
description string
event events.SaltEvent
event event.SaltEvent
datetime string
sender string
state string
Expand Down
6 changes: 3 additions & 3 deletions internal/tui/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
teaViewport "github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/kpetremann/salt-exporter/pkg/events"
"github.com/kpetremann/salt-exporter/pkg/event"
)

const theme = "solarized-dark"
Expand All @@ -28,7 +28,7 @@ type model struct {
eventList teaList.Model
itemsBuffer []teaList.Item
rawView teaViewport.Model
eventChan <-chan events.SaltEvent
eventChan <-chan event.SaltEvent
hardFilter string
keys *keyMap
sideInfos string
Expand All @@ -40,7 +40,7 @@ type model struct {
wordWrap bool
}

func NewModel(eventChan <-chan events.SaltEvent, maxItems int, filter string) model {
func NewModel(eventChan <-chan event.SaltEvent, maxItems int, filter string) model {
var listKeys = defaultKeyMap()

list := teaList.NewDefaultDelegate()
Expand Down
45 changes: 1 addition & 44 deletions pkg/events/events.go → pkg/event/event.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package events
package event

import (
"encoding/json"
"errors"
"strings"

"github.com/rs/zerolog/log"
"github.com/vmihailenco/msgpack/v5"
"gopkg.in/yaml.v3"
)
Expand Down Expand Up @@ -115,44 +113,3 @@ func (e *SaltEvent) ExtractState() string {
}
return ""
}

// ParseEvent parses a salt event
//
// Once parsed, the message is sent to the eventChan channel.
// KeepRawBody is used to keep the raw body of the event.
func ParseEvent(message map[string]interface{}, eventChan chan<- SaltEvent, keepRawBody bool) {
body := string(message["body"].([]byte))
lines := strings.SplitN(body, "\n\n", 2)

tag := lines[0]
if !(strings.HasPrefix(tag, "salt/job") || strings.HasPrefix(tag, "salt/run")) {
return
}
log.Debug().Str("tag", tag).Msg("new event")

// Extract job type from the tag
job_type := strings.Split(tag, "/")[3]

// Parse message body
byteResult := []byte(lines[1])
event := SaltEvent{Tag: tag, Type: job_type}

if keepRawBody {
event.RawBody = byteResult
}

if err := msgpack.Unmarshal(byteResult, &event.Data); err != nil {
log.Warn().Str("error", err.Error()).Str("tag", tag).Msg("decoding_failure")
return
}

event.TargetNumber = len(event.Data.Minions)
event.IsScheduleJob = event.Data.Schedule != ""

// A runner are executed on the master but they do not provide their ID in the event
if strings.HasPrefix(tag, "salt/run") && event.Data.Id == "" {
event.Data.Id = "master"
}

eventChan <- event
}
99 changes: 99 additions & 0 deletions pkg/event/event_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package event_test

import (
"testing"

"github.com/kpetremann/salt-exporter/pkg/event"
)

func getNewStateEvent() event.SaltEvent {
return event.SaltEvent{
Tag: "salt/job/20220630000f000000000/new",
Type: "new",
TargetNumber: 1,
Data: event.EventData{
Timestamp: "2022-06-30T00:00:00.000000",
Fun: "state.sls",
Arg: []interface{}{"test"},
Jid: "20220630000000000000",
Minions: []string{"node1"},
Missing: []string{},
Tgt: "node1",
TgtType: "glob",
User: "salt_user",
},
IsScheduleJob: false,
}
}

func TestExtractState(t *testing.T) {
stateSls := getNewStateEvent()

stateSlsFunArg := getNewStateEvent()
stateSlsFunArg.Data.Arg = nil
stateSlsFunArg.Data.FunArgs = []interface{}{"test", map[string]bool{"dry_run": true}}

stateSlsFunArgMap := getNewStateEvent()
stateSlsFunArgMap.Data.Arg = nil
stateSlsFunArgMap.Data.FunArgs = []interface{}{map[string]interface{}{"mods": "test", "dry_run": true}}

stateApplyArg := getNewStateEvent()
stateApplyArg.Data.Fun = "state.apply"

stateApplyHighstate := getNewStateEvent()
stateApplyHighstate.Data.Fun = "state.apply"
stateApplyHighstate.Data.Arg = nil

stateHighstate := getNewStateEvent()
stateHighstate.Data.Fun = "state.highstate"
stateHighstate.Data.Arg = nil

tests := []struct {
name string
event event.SaltEvent
want string
}{
{
name: "state via state.sls",
event: stateSls,
want: "test",
},
{
name: "state via state.sls args + kwargs",
event: stateSlsFunArg,
want: "test",
},
{
name: "state via state.sls kwargs only",
event: stateSlsFunArgMap,
want: "test",
},
{
name: "state via state.apply args only",
event: stateApplyArg,
want: "test",
},
{
name: "state via state.apply",
event: stateApplyArg,
want: "test",
},
{
name: "highstate via state.apply",
event: stateApplyHighstate,
want: "highstate",
},
{
name: "state.highstate",
event: stateHighstate,
want: "highstate",
},
}

for _, test := range tests {
if res := test.event.ExtractState(); res != test.want {
t.Errorf("Mismatch for '%s', wants '%s' got '%s' ", test.name, test.want, res)
}
}

}
Loading