Skip to content

Commit

Permalink
feat(server): add prometheus stats server
Browse files Browse the repository at this point in the history
  • Loading branch information
aymanbagabas committed May 2, 2023
1 parent 27a5e47 commit ae9cc3e
Show file tree
Hide file tree
Showing 10 changed files with 674 additions and 15 deletions.
12 changes: 11 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ go 1.17

require (
github.com/alecthomas/chroma v0.10.0
github.com/caarlos0/env/v6 v6.10.1
github.com/charmbracelet/bubbles v0.15.0
github.com/charmbracelet/bubbletea v0.23.2
github.com/charmbracelet/glamour v0.6.0
Expand All @@ -20,6 +19,7 @@ require (

require (
github.com/aymanbagabas/go-osc52 v1.2.2
github.com/caarlos0/env/v7 v7.1.0
github.com/charmbracelet/keygen v0.3.0
github.com/charmbracelet/log v0.2.1
github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103
Expand All @@ -28,6 +28,8 @@ require (
github.com/lrstanley/bubblezone v0.0.0-20220716194435-3cb8c52f6a8f
github.com/muesli/mango-cobra v1.2.0
github.com/muesli/roff v0.1.0
github.com/prometheus/client_golang v1.14.0
github.com/robfig/cron/v3 v3.0.1
github.com/spf13/cobra v1.6.1
goji.io v2.0.2+incompatible
golang.org/x/crypto v0.7.0
Expand All @@ -40,16 +42,20 @@ require (
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/caarlos0/sshmarshal v0.1.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/containerd/console v1.0.3 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75 // indirect
github.com/microcosm-cc/bluemonday v1.0.21 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
Expand All @@ -58,6 +64,9 @@ require (
github.com/muesli/mango v0.1.0 // indirect
github.com/muesli/mango-pflag v0.1.0 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/sahilm/fuzzy v0.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
Expand All @@ -67,4 +76,5 @@ require (
golang.org/x/sys v0.6.0 // indirect
golang.org/x/term v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
)
449 changes: 447 additions & 2 deletions go.sum

Large diffs are not rendered by default.

18 changes: 15 additions & 3 deletions server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"os"
"path/filepath"

"github.com/caarlos0/env/v6"
"github.com/caarlos0/env/v7"
"github.com/charmbracelet/log"
"github.com/charmbracelet/soft-serve/server/backend"
"gopkg.in/yaml.v3"
Expand Down Expand Up @@ -61,6 +61,12 @@ type HTTPConfig struct {
PublicURL string `env:"PUBLIC_URL" yaml:"public_url"`
}

// StatsConfig is the configuration for the stats server.
type StatsConfig struct {
// ListenAddr is the address on which the stats server will listen.
ListenAddr string `env:"LISTEN_ADDR" yaml:"listen_addr"`
}

// Config is the configuration for Soft Serve.
type Config struct {
// Name is the name of the server.
Expand All @@ -75,6 +81,9 @@ type Config struct {
// HTTP is the configuration for the HTTP server.
HTTP HTTPConfig `envPrefix:"HTTP_" yaml:"http"`

// Stats is the configuration for the stats server.
Stats StatsConfig `envPrefix:"STATS_" yaml:"stats"`

// InitialAdminKeys is a list of public keys that will be added to the list of admins.
InitialAdminKeys []string `env:"INITIAL_ADMIN_KEY" envSeparator:"\n" yaml:"initial_admin_keys"`

Expand All @@ -92,7 +101,7 @@ func ParseConfig(path string) (*Config, error) {
if err != nil {
return nil, err
}
defer f.Close()
defer f.Close() // nolint: errcheck
if err := yaml.NewDecoder(f).Decode(cfg); err != nil {
return nil, err
}
Expand Down Expand Up @@ -129,11 +138,14 @@ func DefaultConfig() *Config {
ListenAddr: ":8080",
PublicURL: "http://localhost:8080",
},
Stats: StatsConfig{
ListenAddr: ":8081",
},
}
cp := filepath.Join(cfg.DataPath, "config.yaml")
f, err := os.Open(cp)
if err == nil {
defer f.Close()
defer f.Close() // nolint: errcheck
if err := yaml.NewDecoder(f).Decode(cfg); err != nil {
log.Error("failed to decode config", "err", err)
}
Expand Down
5 changes: 5 additions & 0 deletions server/config/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ http:
# This is the address will be used to clone repositories.
public_url: "{{ .HTTP.PublicURL }}"
# The stats server configuration.
stats:
# The address on which the stats server will listen.
listen_addr: "{{ .Stats.ListenAddr }}"
`))
)

Expand Down
22 changes: 22 additions & 0 deletions server/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,24 @@ import (
"github.com/charmbracelet/soft-serve/server/config"
"github.com/charmbracelet/soft-serve/server/utils"
"github.com/go-git/go-git/v5/plumbing/format/pktline"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)

var (
uploadPackGitCounter = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: "soft_serve",
Subsystem: "git",
Name: "git_upload_pack_total",
Help: "The total number of git-upload-pack requests",
}, []string{"repo"})

uploadArchiveGitCounter = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: "soft_serve",
Subsystem: "git",
Name: "git_upload_archive_total",
Help: "The total number of git-upload-archive requests",
}, []string{"repo"})
)

// ErrServerClosed indicates that the server has been closed.
Expand Down Expand Up @@ -184,12 +202,14 @@ func (d *GitDaemon) handleClient(conn net.Conn) {
}

gitPack := uploadPack
counter := uploadPackGitCounter
cmd := string(split[0])
switch cmd {
case uploadPackBin:
gitPack = uploadPack
case uploadArchiveBin:
gitPack = uploadArchive
counter = uploadArchiveGitCounter
default:
fatal(c, ErrInvalidRequest)
return
Expand Down Expand Up @@ -223,6 +243,8 @@ func (d *GitDaemon) handleClient(conn net.Conn) {
fatal(c, err)
return
}

counter.WithLabelValues(name)
}
}

Expand Down
25 changes: 23 additions & 2 deletions server/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,29 @@ import (
"github.com/charmbracelet/soft-serve/server/config"
"github.com/charmbracelet/soft-serve/server/utils"
"github.com/dustin/go-humanize"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"goji.io"
"goji.io/pat"
"goji.io/pattern"
)

var (
gitHttpCounter = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: "soft_serve",
Subsystem: "http",
Name: "git_fetch_pull_total",
Help: "The total number of git fetch/pull requests",
}, []string{"repo", "file"})

goGetCounter = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: "soft_serve",
Subsystem: "http",
Name: "go_get_total",
Help: "The total number of go get requests",
}, []string{"repo"})
)

// logWriter is a wrapper around http.ResponseWriter that allows us to capture
// the HTTP status code and bytes written to the response.
type logWriter struct {
Expand Down Expand Up @@ -179,12 +197,12 @@ var (
return findStringSubmatch(u.Path, getLooseObjectRegexp)
}

getPackFileRegexp = regexp.MustCompile(".*?(/objects/pack/pack-[0-9a-f]{40}\\.pack)$")
getPackFileRegexp = regexp.MustCompile(`.*?(/objects/pack/pack-[0-9a-f]{40}\.pack)$`)
getPackFile = func(u *url.URL) *Match {
return findStringSubmatch(u.Path, getPackFileRegexp)
}

getIdxFileRegexp = regexp.MustCompile(".*?(/objects/pack/pack-[0-9a-f]{40}\\.idx)$")
getIdxFileRegexp = regexp.MustCompile(`.*?(/objects/pack/pack-[0-9a-f]{40}\.idx)$`)
getIdxFile = func(u *url.URL) *Match {
return findStringSubmatch(u.Path, getIdxFileRegexp)
}
Expand Down Expand Up @@ -264,6 +282,8 @@ func (s *HTTPServer) handleIndex(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

goGetCounter.WithLabelValues(repo).Inc()
}

func (s *HTTPServer) handleGit(w http.ResponseWriter, r *http.Request) {
Expand All @@ -287,6 +307,7 @@ func (s *HTTPServer) handleGit(w http.ResponseWriter, r *http.Request) {
}

file := pat.Param(r, "file")
gitHttpCounter.WithLabelValues(repo, file).Inc()
r.URL.Path = fmt.Sprintf("/%s/%s", repo, file)
s.dirHandler.ServeHTTP(w, r)
}
27 changes: 22 additions & 5 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ var (

// Server is the Soft Serve server.
type Server struct {
SSHServer *SSHServer
GitDaemon *GitDaemon
HTTPServer *HTTPServer
Config *config.Config
Backend backend.Backend
SSHServer *SSHServer
GitDaemon *GitDaemon
HTTPServer *HTTPServer
StatsServer *StatsServer
Config *config.Config
Backend backend.Backend
}

// NewServer returns a new *ssh.Server configured to serve Soft Serve. The SSH
Expand Down Expand Up @@ -74,6 +75,11 @@ func NewServer(cfg *config.Config) (*Server, error) {
return nil, err
}

srv.StatsServer, err = NewStatsServer(cfg)
if err != nil {
return nil, err
}

return srv, nil
}

Expand Down Expand Up @@ -101,6 +107,13 @@ func (s *Server) Start() error {
}
return nil
})
errg.Go(func() error {
log.Print("Starting Stats server", "addr", s.Config.Stats.ListenAddr)
if err := s.StatsServer.ListenAndServe(); err != http.ErrServerClosed {
return err
}
return nil
})
return errg.Wait()
}

Expand All @@ -116,6 +129,9 @@ func (s *Server) Shutdown(ctx context.Context) error {
errg.Go(func() error {
return s.SSHServer.Shutdown(ctx)
})
errg.Go(func() error {
return s.StatsServer.Shutdown(ctx)
})
return errg.Wait()
}

Expand All @@ -125,5 +141,6 @@ func (s *Server) Close() error {
errg.Go(s.GitDaemon.Close)
errg.Go(s.HTTPServer.Close)
errg.Go(s.SSHServer.Close)
errg.Go(s.StatsServer.Close)
return errg.Wait()
}
17 changes: 17 additions & 0 deletions server/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,28 @@ import (
"github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
bm "github.com/charmbracelet/wish/bubbletea"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)

var (
tuiSessionCounter = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: "soft_serve",
Subsystem: "ssh",
Name: "tui_session_total",
Help: "The total number of TUI sessions",
}, []string{"key", "user", "repo", "term"})
)

// SessionHandler is the soft-serve bubbletea ssh session handler.
func SessionHandler(cfg *config.Config) bm.ProgramHandler {
return func(s ssh.Session) *tea.Program {
ak := backend.MarshalAuthorizedKey(s.PublicKey())
pty, _, active := s.Pty()
if !active {
return nil
}

cmd := s.Command()
initialRepo := ""
if len(cmd) == 1 {
Expand All @@ -32,6 +45,7 @@ func SessionHandler(cfg *config.Config) bm.ProgramHandler {
return nil
}
}

envs := s.Environ()
envs = append(envs, fmt.Sprintf("TERM=%s", pty.Term))
output := osc52.NewOutput(s, envs)
Expand All @@ -45,6 +59,9 @@ func SessionHandler(cfg *config.Config) bm.ProgramHandler {
tea.WithoutCatchPanics(),
tea.WithMouseCellMotion(),
)

tuiSessionCounter.WithLabelValues(ak, s.User(), initialRepo, pty.Term).Inc()

return p
}
}

0 comments on commit ae9cc3e

Please sign in to comment.