Skip to content

Commit

Permalink
feat: load config from env
Browse files Browse the repository at this point in the history
  • Loading branch information
luissimas committed May 28, 2024
1 parent 352f850 commit d815bfb
Show file tree
Hide file tree
Showing 7 changed files with 631 additions and 16 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ build:
go build -o bin/$(BINARY_NAME) ./cmd/zettelkasten-exporter/main.go

run: build
./bin/$(BINARY_NAME) ~/Documents/vault/
ZETTELKASTEN_DIRECTORY=~/Documents/vault ./bin/$(BINARY_NAME)
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
- [X] Register links per note
- [X] Expose prometheus metrics endpoint
- [X] Run periodically
- [ ] Read config
- [X] Read config
- [ ] Find all files recursivelly
- [ ] Configurable ignore file patterns
- [ ] Handle uncreated links
- [ ] Collect backlinks
- [ ] Configurable ignore file patterns
- [ ] Parse markdown links
- [ ] Get zettelkasten from git url
- [ ] Support private repositories (Maybe with Github's PAT?)

https://prometheus.io/docs/instrumenting/writing_exporters/#metrics
20 changes: 9 additions & 11 deletions cmd/zettelkasten-exporter/main.go
Original file line number Diff line number Diff line change
@@ -1,35 +1,33 @@
package main

import (
"fmt"
"log/slog"
"net/http"
"os"
"path/filepath"
"time"

"github.com/luissimas/zettelkasten-exporter/internal/collector"
"github.com/luissimas/zettelkasten-exporter/internal/config"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

func main() {
if len(os.Args) != 2 {
slog.Error("Wrong arguments")
os.Exit(1)
}
dir := os.Args[1]
absolute_path, err := filepath.Abs(dir)
cfg, err := config.LoadConfig()
if err != nil {
slog.Error("Error getting absolute file path", slog.Any("error", err))
slog.Error("Error loading config", slog.Any("error", err))
os.Exit(1)
}
interval, _ := time.ParseDuration("5m")
collector := collector.NewCollector(absolute_path, interval)
slog.Info("Loaded config", slog.Any("config", cfg))

absolute_path, err := filepath.Abs(cfg.ZettelkastenDirectory)
collector := collector.NewCollector(absolute_path, cfg.ScrapeInterval)

slog.Info("Starting metrics collector")
collector.StartCollecting()
slog.Info("Metrics collector started")

addr := "0.0.0.0:6969"
addr := fmt.Sprintf("%s:%d", cfg.IP, cfg.Port)
http.Handle("/metrics", promhttp.Handler())
slog.Info("Starting HTTP server", slog.String("address", addr))
err = http.ListenAndServe(addr, nil)
Expand Down
18 changes: 17 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,30 @@ module github.com/luissimas/zettelkasten-exporter

go 1.22.2

require github.com/prometheus/client_golang v1.19.1
require (
github.com/gookit/validate v1.5.2
github.com/knadh/koanf v1.5.0
github.com/prometheus/client_golang v1.19.1
)

require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gookit/filter v1.2.1 // indirect
github.com/gookit/goutil v0.6.15 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/stretchr/testify v1.8.4 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/term v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
)
402 changes: 401 additions & 1 deletion go.sum

Large diffs are not rendered by default.

52 changes: 52 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package config

import (
"log/slog"
"strings"
"time"

"github.com/gookit/validate"
"github.com/knadh/koanf"
"github.com/knadh/koanf/providers/env"
"github.com/knadh/koanf/providers/structs"
)

type Config struct {
IP string `koanf:"ip" validate:"required|ip"`
Port int `koanf:"port" validate:"required|uint"`
LogLevel slog.Level `koanf:"log_level"`
ScrapeInterval time.Duration `koanf:"scrape_interval" validate:"required"`
ZettelkastenDirectory string `koanf:"zettelkasten_directory" validate:"required"`
}

func LoadConfig() (Config, error) {
k := koanf.New(".")

// Set default values
k.Load(structs.Provider(Config{
IP: "0.0.0.0",
Port: 6969,
LogLevel: slog.LevelInfo,
ScrapeInterval: time.Minute * 5,
}, "koanf"), nil)

// Load env variables
k.Load(env.Provider("", ".", strings.ToLower), nil)

// Unmarshal into config struct
var cfg Config
k.Unmarshal("", &cfg)

// Validate config
v := validate.Struct(cfg)
v.AddValidator("duration", validateDuration)
if !v.Validate() {
return Config{}, v.Errors
}

return cfg, nil
}

func validateDuration(val time.Duration) bool {
return val > 0
}
146 changes: 146 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package config

import (
"log/slog"
"testing"
"time"
)

func TestLoadConfig_DefaultValues(t *testing.T) {
t.Setenv("ZETTELKASTEN_DIRECTORY", "/any/dir")
c, err := LoadConfig()
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}

expected := Config{
IP: "0.0.0.0",
Port: 6969,
LogLevel: slog.LevelInfo,
ScrapeInterval: time.Minute * 5,
ZettelkastenDirectory: "/any/dir",
}
if c != expected {
t.Errorf("Expected %v, got: %v", expected, c)
}
}

func TestLoadConfig_PartialEnv(t *testing.T) {
t.Setenv("PORT", "4444")
t.Setenv("LOG_LEVEL", "DEBUG")
t.Setenv("ZETTELKASTEN_DIRECTORY", "/any/dir")
c, err := LoadConfig()
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}

expected := Config{
IP: "0.0.0.0",
Port: 4444,
LogLevel: slog.LevelDebug,
ScrapeInterval: time.Minute * 5,
ZettelkastenDirectory: "/any/dir",
}
if c != expected {
t.Errorf("Expected %v, got: %v", expected, c)
}
}

func TestLoadConfig_FullEnv(t *testing.T) {
t.Setenv("IP", "127.0.0.1")
t.Setenv("PORT", "4444")
t.Setenv("LOG_LEVEL", "DEBUG")
t.Setenv("SCRAPE_INTERVAL", "5m")
t.Setenv("ZETTELKASTEN_DIRECTORY", "/any/dir")
c, err := LoadConfig()
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}

expected := Config{
IP: "127.0.0.1",
Port: 4444,
LogLevel: slog.LevelDebug,
ScrapeInterval: time.Minute * 5,
ZettelkastenDirectory: "/any/dir",
}
if c != expected {
t.Errorf("Expected %v, got: %v", expected, c)
}
}

func TestLoadConfig_Validate(t *testing.T) {
data := []struct {
name string
shouldError bool
env map[string]string
}{
{
name: "missing directory",
shouldError: true,
env: map[string]string{
"IP": "0.0.0.0",
"PORT": "4444",
"LOG_LEVEL": "INFO",
},
},
{
name: "invalid ip",
shouldError: true,
env: map[string]string{
"IP": "any-string",
"PORT": "4444",
"LOG_LEVEL": "INFO",
"SCRAPE_INTERVAL": "5m",
"ZETTELKASTEN_DIRECTORY": "/any/dir",
},
},
{
name: "invalid port",
shouldError: true,
env: map[string]string{
"IP": "0.0.0.0",
"PORT": "-1",
"LOG_LEVEL": "INFO",
"SCRAPE_INTERVAL": "5m",
"ZETTELKASTEN_DIRECTORY": "/any/dir",
},
},
{
name: "invalid interval",
shouldError: true,
env: map[string]string{
"IP": "0.0.0.0",
"PORT": "4444",
"LOG_LEVEL": "INFO",
"SCRAPE_INTERVAL": "5",
"ZETTELKASTEN_DIRECTORY": "/any/dir",
},
},
{
name: "valid config",
shouldError: false,
env: map[string]string{
"IP": "0.0.0.0",
"PORT": "4444",
"LOG_LEVEL": "INFO",
"SCRAPE_INTERVAL": "5m",
"ZETTELKASTEN_DIRECTORY": "/any/dir",
},
},
}

for _, d := range data {
t.Run(d.name, func(t *testing.T) {
for k, v := range d.env {
t.Setenv(k, v)
}
_, err := LoadConfig()
if err == nil && d.shouldError {
t.Errorf("Expected error, got: %v", err)
} else if err != nil && !d.shouldError {
t.Errorf("Expected no error, got: %v", err)
}
})
}
}

0 comments on commit d815bfb

Please sign in to comment.