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

Feat/cfg loader #5

Merged
merged 10 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from 9 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
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ jobs:
- name: Test
run: |
go mod download
go install github.com/matryer/[email protected]
go generate ./...
go test --race --coverprofile cover.out -v ./...
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@
# Go workspace file
go.work
gen

# generated interface mock files
*_moq.go
7 changes: 3 additions & 4 deletions cmd/run/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@ import (
"context"
"log"

"github.com/caas-team/sparrow/pkg/config"
"github.com/caas-team/sparrow/pkg/sparrow"
)

func main() {

config := sparrow.NewConfig()
sparrow := sparrow.New(config)
cfg := config.NewConfig()
sparrow := sparrow.New(cfg)

log.Println("running sparrow")
if err := sparrow.Run(context.Background()); err != nil {
panic(err)
}

}
8 changes: 7 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ go 1.20

require github.com/getkin/kin-openapi v0.120.0

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
)

require (
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
Expand All @@ -12,5 +17,6 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
github.com/stretchr/testify v1.8.4
gopkg.in/yaml.v3 v3.0.1
)
3 changes: 2 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
Expand Down
5 changes: 2 additions & 3 deletions pkg/checks/checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import (
// Available Checks will be registered in this map
// The key is the name of the Check
// The name needs to map the configuration item key
var RegisteredChecks = map[string]func(string) Check{
var RegisteredChecks = map[string]func() Check{
"rtt": GetRoundtripCheck,
}

//go:generate moq -out checks_moq.go . Check
type Check interface {
// Run is called once per check interval
// this should error if there is a problem running the check
Expand All @@ -29,8 +30,6 @@ type Check interface {
// This is also called while the check is running, if the remote config is updated
// This should return an error if the config is invalid
SetConfig(ctx context.Context, config any) error
// Name returns the name of the check
Name() string
// Should return an openapi3.SchemaRef of the result type returned by the check
Schema() (*openapi3.SchemaRef, error)
}
Expand Down
12 changes: 2 additions & 10 deletions pkg/checks/roundtrip.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,13 @@ type roundTripData struct {

// RoundTrip is a check that measures the round trip time of a request
type RoundTrip struct {
name string
c chan<- Result
config RoundTripConfig
}

// Constructor for the RoundtripCheck
func GetRoundtripCheck(name string) Check {
return &RoundTrip{
name: name,
}
func GetRoundtripCheck() Check {
return &RoundTrip{}
}

func (rt *RoundTrip) Run(ctx context.Context) (Result, error) {
Expand All @@ -51,11 +48,6 @@ func (rt *RoundTrip) Shutdown(ctx context.Context) error {
return nil
}

// Name returns the name of the check
func (rt *RoundTrip) Name() string {
return rt.name
}

func (rt *RoundTrip) SetConfig(ctx context.Context, config any) error {
checkConfig, ok := config.(RoundTripConfig)
if !ok {
Expand Down
5 changes: 3 additions & 2 deletions pkg/sparrow/config.go → pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package sparrow
package config

type Config struct {
Checks map[string]any
Checks map[string]any
Updated chan bool
y-eight marked this conversation as resolved.
Show resolved Hide resolved
}

func NewConfig() *Config {
Expand Down
26 changes: 26 additions & 0 deletions pkg/config/gitlab.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package config
y-eight marked this conversation as resolved.
Show resolved Hide resolved

import "context"

type HttpLoader struct {
cfg *Config
cCfgChecks chan<- map[string]any
}

func NewHttpLoader(cfg *Config, cCfgChecks chan<- map[string]any) *HttpLoader {
return &HttpLoader{
cfg: cfg,
cCfgChecks: cCfgChecks,
}
}

func (gl *HttpLoader) Run(ctx context.Context) {
// Get cfg from gitlab
// check cfg has changed
// send signal

// cfg has changed
gl.cCfgChecks <- map[string]any{
"rtt": "check cfg to set dynamically",
}
}
24 changes: 24 additions & 0 deletions pkg/config/loader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package config

import (
"context"
"strings"
)

const (
gitlabLoader = "GITLAB"
localLoader = "LOCAL"
)

type Loader interface {
Run(context.Context)
}

func NewLoader(cfg *Config, cCfgChecks chan<- map[string]any) Loader {
switch strings.ToUpper("cfg.loaderTyp") {
y-eight marked this conversation as resolved.
Show resolved Hide resolved
case gitlabLoader:
return NewHttpLoader(cfg, cCfgChecks)
default:
return NewHttpLoader(cfg, cCfgChecks)
}
}
94 changes: 64 additions & 30 deletions pkg/sparrow/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,90 @@ package sparrow
import (
"context"
"fmt"
"log"

"github.com/caas-team/sparrow/pkg/checks"
"github.com/caas-team/sparrow/pkg/config"
"github.com/getkin/kin-openapi/openapi3"
)

type Sparrow struct {
checks []checks.Check
config *Config
c chan checks.Result
checks map[string]checks.Check
cResult chan checks.Result

loader config.Loader
cfg *config.Config
cCfgChecks chan map[string]any
}

// New creates a new sparrow from a given configfile
func New(config *Config) *Sparrow {
s := &Sparrow{
config: config,
c: make(chan checks.Result),
func New(cfg *config.Config) *Sparrow {
// TODO read this from config file
sparrow := &Sparrow{
checks: make(map[string]checks.Check),
cResult: make(chan checks.Result),
cfg: cfg,
cCfgChecks: make(chan map[string]any),
}

return s
sparrow.loader = config.NewLoader(cfg, sparrow.cCfgChecks)
return sparrow
}

// Run starts the sparrow
func (s *Sparrow) Run(ctx context.Context) error {
// TODO Setup before checks run
// setup database
// setup http server
for {
select {
case <-ctx.Done():
return nil
case result := <-s.cResult:
// TODO write result to database
fmt.Println(result)
case configChecks := <-s.cCfgChecks:
// Config got updated
// Set checks
s.cfg.Checks = configChecks
s.ReconceilChecks(ctx)
}
}
}

for checkName, checkConfig := range s.config.Checks {
check := checks.RegisteredChecks[checkName](checkName)
s.checks = append(s.checks, check)
// Register new Checks, unregister removed Checks & reset Configs of Checks
func (s *Sparrow) ReconceilChecks(ctx context.Context) {
y-eight marked this conversation as resolved.
Show resolved Hide resolved
for name, check := range s.cfg.Checks {
if existingCheck, ok := s.checks[name]; ok {
// Check already registered, reset config
err := existingCheck.SetConfig(ctx, check)
if err != nil {
log.Printf("Failed to reset config for check, check will run with last applies config - %s: %s", name, err.Error())
}
continue
}
// Check is a new Check and needs to be registered
check := checks.RegisteredChecks[name]()
s.checks[name] = check

err := check.SetConfig(ctx, checkConfig)
err := check.SetConfig(ctx, check)
if err != nil {
return fmt.Errorf("failed to set config for check %s: %w", check.Name(), err)
log.Printf("Failed to set config for check %s: %s", name, err.Error())
}
err = check.Startup(ctx, s.c)
err = check.Startup(ctx, s.cResult)
if err != nil {
return fmt.Errorf("failed to startup check %s: %w", check.Name(), err)
log.Printf("Failed to startup check %s: %s", name, err.Error())
}
go check.Run(ctx)
}
for {
select {
case <-ctx.Done():
return nil
case result := <-s.c:
// TODO write result to database
fmt.Println(result)

for existingCheckName, existingCheck := range s.checks {
y-eight marked this conversation as resolved.
Show resolved Hide resolved
// Check has been removed from config; shutdown and remove
if _, ok := s.cfg.Checks[existingCheckName]; !ok {
existingCheck.Shutdown(ctx)
delete(s.checks, existingCheckName)
}
}

}

var oapiBoilerplate = openapi3.T{
Expand All @@ -76,19 +110,19 @@ var oapiBoilerplate = openapi3.T{

func (s *Sparrow) Openapi() (openapi3.T, error) {
doc := oapiBoilerplate
for _, c := range s.checks {
for name, c := range s.checks {
ref, err := c.Schema()
if err != nil {
return openapi3.T{}, fmt.Errorf("failed to get schema for check %s: %w", c.Name(), err)
return openapi3.T{}, fmt.Errorf("failed to get schema for check %s: %w", name, err)
}

routeDesc := fmt.Sprintf("Returns the performance data for check %s", c.Name())
bodyDesc := fmt.Sprintf("Metrics for check %s", c.Name())
doc.Paths["/v1/metrics/"+c.Name()] = &openapi3.PathItem{
Description: c.Name(),
routeDesc := fmt.Sprintf("Returns the performance data for check %s", name)
bodyDesc := fmt.Sprintf("Metrics for check %s", name)
doc.Paths["/v1/metrics/"+name] = &openapi3.PathItem{
Description: name,
Get: &openapi3.Operation{
Description: routeDesc,
Tags: []string{"Metrics", c.Name()},
Tags: []string{"Metrics", name},
Responses: openapi3.Responses{
"200": &openapi3.ResponseRef{
Value: &openapi3.Response{
Expand Down
Loading