Skip to content

Commit

Permalink
feat!: add prometheus metrics support
Browse files Browse the repository at this point in the history
- Add arguments `-prometheus-metrics-enabled` and
  `-prometheus-listen-addr` to control whether to expose metrics from
  the registry app.
- The `ModuleStore` interface has received an extra method `Metrics()`
  to support store-specific metrics.
  • Loading branch information
stigok committed Feb 27, 2024
1 parent b7e28e8 commit 5312f4b
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 16 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ Command line arguments:
- `-tls-enabled`: Whether to enable TLS termination (default: `false`)
- `-tls-cert-file`: Path to TLS certificate file
- `-tls-key-file`: Path to TLS certificate private key file
- `-prometheus-listen-addr`: Listen address where /metrics endpoint is exposed (default: `:9090`)
- `-prometheus-metrics-enabled`: Enable the Prometheus metrics endpoint (default: `false`)
- `-log-level`: Log level selection: `debug`, `info`, `warn`, `error` (default: `info`)
- `-log-format`: Log output format selection: `json`, `console` (default: `console`)
- `-version`: Print version info and exit
Expand Down
69 changes: 56 additions & 13 deletions cmd/terraform-registry/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,27 @@ import (
"github.com/nrkno/terraform-registry/pkg/registry"
"github.com/nrkno/terraform-registry/pkg/store/github"
"github.com/nrkno/terraform-registry/pkg/store/s3"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.uber.org/zap"
)

var (
listenAddr string
accessLogDisabled bool
accessLogIgnoredPaths string
authDisabled bool
authTokensFile string
envJSONFiles string
tlsEnabled bool
tlsCertFile string
tlsKeyFile string
storeType string
logLevelStr string
logFormatStr string
printVersionInfo bool
listenAddr string
accessLogDisabled bool
accessLogIgnoredPaths string
authDisabled bool
authTokensFile string
envJSONFiles string
tlsEnabled bool
tlsCertFile string
tlsKeyFile string
storeType string
prometheusListenAddr string
prometheusMetricsEnabled bool
logLevelStr string
logFormatStr string
printVersionInfo bool

S3Region string
S3Bucket string
Expand Down Expand Up @@ -84,6 +88,8 @@ func init() {
flag.StringVar(&logLevelStr, "log-level", "info", "Levels: debug, info, warn, error")
flag.StringVar(&logFormatStr, "log-format", "console", "Formats: json, console")
flag.BoolVar(&printVersionInfo, "version", false, "Print version info and exit")
flag.StringVar(&prometheusListenAddr, "prometheus-listen-addr", ":9090", "Listen address where /metrics endpoint is exposed.")
flag.BoolVar(&prometheusMetricsEnabled, "prometheus-metrics-enabled", false, "Enable the Prometheus metrics endpoint.")

flag.StringVar(&gitHubOwnerFilter, "github-owner-filter", "", "GitHub org/user repository filter")
flag.StringVar(&gitHubTopicFilter, "github-topic-filter", "", "GitHub topic repository filter")
Expand Down Expand Up @@ -187,6 +193,16 @@ func main() {
logger.Fatal("invalid store type", zap.String("selected", storeType))
}

if prometheusMetricsEnabled {
prometheus.
WrapRegistererWithPrefix("terraform_registry_", prometheus.DefaultRegisterer).
MustRegister(reg.Metrics())

mux := http.NewServeMux()
mux.Handle("/metrics", promhttp.Handler())
go prometheusListenAndServe(mux)
}

listenAndServe(reg)
}

Expand Down Expand Up @@ -217,6 +233,33 @@ func listenAndServe(handler http.Handler) {
}
}

func prometheusListenAndServe(handler http.Handler) {
srv := http.Server{
Addr: prometheusListenAddr,
Handler: handler,
ReadTimeout: 3 * time.Second,
ReadHeaderTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
IdleTimeout: 60 * time.Second, // keep-alive timeout
}

logger.Info("starting Prometheus metrics HTTP server",
zap.Bool("tls", tlsEnabled),
zap.String("listenAddr", prometheusListenAddr),
)
if tlsEnabled {
err := srv.ListenAndServeTLS(tlsCertFile, tlsKeyFile)
logger.Panic("prometheusListenAndServe",
zap.Errors("err", []error{err}),
)
} else {
err := srv.ListenAndServe()
logger.Panic("prometheusListenAndServe",
zap.Errors("err", []error{err}),
)
}
}

// watchFile reads the contents of the file at `filename`, first immediately, then at at every `interval`.
// If and only if the file contents have changed since the last invocation of `callback` it is called again.
// Note that the callback will always be called initially when this function is called.
Expand Down
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,31 @@ require (
github.com/hashicorp/go-version v1.6.0
github.com/matryer/is v1.4.1
github.com/migueleliasweb/go-github-mock v0.0.23
github.com/prometheus/client_golang v1.18.0
github.com/stretchr/testify v1.8.4
go.uber.org/zap v1.27.0
golang.org/x/oauth2 v0.17.0
)

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.1 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-github/v59 v59.0.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.31.0 // indirect
Expand Down
28 changes: 26 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
github.com/aws/aws-sdk-go v1.50.26 h1:tuv8+dje59DBK1Pj65tSCdD36oamBxKYJgbng4bFylc=
github.com/aws/aws-sdk-go v1.50.26/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -27,12 +31,28 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
github.com/migueleliasweb/go-github-mock v0.0.23 h1:GOi9oX/+Seu9JQ19V8bPDLqDI7M9iEOjo3g8v1k6L2c=
github.com/migueleliasweb/go-github-mock v0.0.23/go.mod h1:NsT8FGbkvIZQtDu38+295sZEX8snaUiiQgsGxi6GUxk=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
Expand All @@ -56,6 +76,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
Expand All @@ -70,10 +92,12 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
7 changes: 6 additions & 1 deletion pkg/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@

package core

import "context"
import (
"context"

"github.com/prometheus/client_golang/prometheus"
)

type ModuleVersion struct {
// Version is a SemVer version string that specifies the version for a module.
Expand All @@ -19,4 +23,5 @@ type ModuleVersion struct {
type ModuleStore interface {
ListModuleVersions(ctx context.Context, namespace, name, provider string) ([]*ModuleVersion, error)
GetModuleVersion(ctx context.Context, namespace, name, provider, version string) (*ModuleVersion, error)
Metrics() prometheus.Collector
}
12 changes: 12 additions & 0 deletions pkg/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/nrkno/terraform-registry/pkg/core"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -152,6 +153,17 @@ func (reg *Registry) RequestLogger() func(next http.Handler) http.Handler {

// SPDX-SnippetEnd

// Metrics returns a Prometheus registry containing metrics for the module store.
func (reg *Registry) Metrics() prometheus.Collector {
root := prometheus.NewRegistry()

if reg.moduleStore != nil {
prometheus.WrapRegistererWithPrefix("store_module_", root).MustRegister(reg.moduleStore.Metrics())
}

return root
}

func (reg *Registry) ServeHTTP(w http.ResponseWriter, r *http.Request) {
reg.router.ServeHTTP(w, r)
}
Expand Down
6 changes: 6 additions & 0 deletions pkg/store/github/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/google/go-github/v43/github"
goversion "github.com/hashicorp/go-version"
"github.com/nrkno/terraform-registry/pkg/core"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/zap"
"golang.org/x/oauth2"
)
Expand Down Expand Up @@ -200,3 +201,8 @@ func (s *GitHubStore) searchRepositories(ctx context.Context) ([]*github.Reposit

return allRepos, nil
}

// Metrics returns a registry with metrics for this store.
func (s *GitHubStore) Metrics() prometheus.Collector {
return nil
}
5 changes: 5 additions & 0 deletions pkg/store/memory/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"sync"

"github.com/nrkno/terraform-registry/pkg/core"
"github.com/prometheus/client_golang/prometheus"
)

// MemoryStore is an in-memory store implementation without a backend.
Expand Down Expand Up @@ -72,3 +73,7 @@ func (s *MemoryStore) GetModuleVersion(ctx context.Context, namespace, name, pro

return nil, fmt.Errorf("version '%s' not found for module '%s'", version, key)
}

func (s *MemoryStore) Metrics() prometheus.Collector {
return nil
}
5 changes: 5 additions & 0 deletions pkg/store/s3/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3iface"
"github.com/nrkno/terraform-registry/pkg/core"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -144,3 +145,7 @@ func isValidModuleSourcePath(path string) bool {
r := regexp.MustCompile("^" + addrRegExp + "/" + verRegExp)
return r.MatchString(path)
}

func (s *S3Store) Metrics() prometheus.Collector {
return nil
}

0 comments on commit 5312f4b

Please sign in to comment.