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

one listen port for metrics and another for ingress #73

Merged
merged 3 commits into from
Mar 10, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ name: ci

on:
push:
branches:
- main
pull_request:

jobs:
Expand All @@ -14,6 +16,7 @@ jobs:
with:
go-version: '1.20'
check-latest: true
cache: true

- name: Tests
run: make test
Expand All @@ -30,6 +33,7 @@ jobs:
with:
go-version: '1.20'
check-latest: true
cache: true
- name: golangci-lint
uses: golangci/golangci-lint-action@08e2f20817b15149a52b5b3ebe7de50aff2ba8c5 # v3.4.0
timeout-minutes: 5
Expand Down
64 changes: 46 additions & 18 deletions internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ import (
)

type Opts struct {
MetricsPath string
ListenAddress string
WebhookPath string
MetricsPath string
ListenAddressMetrics string
ListenAddressIngress string
WebhookPath string
// GitHub webhook token.
GitHubToken string
// GitHub API token.
Expand All @@ -30,17 +31,17 @@ type Opts struct {

type Server struct {
logger log.Logger
server *http.Server
serverMetrics *http.Server
serverIngress *http.Server
workflowMetricsExporter *WorkflowMetricsExporter
billingExporter *BillingMetricsExporter
opts Opts
}

func NewServer(logger log.Logger, opts Opts) *Server {
mux := http.NewServeMux()

httpServer := &http.Server{
Handler: mux,
muxMetrics := http.NewServeMux()
httpServerMetrics := &http.Server{
Handler: muxMetrics,
ReadHeaderTimeout: 10 * time.Second,
}

Expand All @@ -54,39 +55,67 @@ func NewServer(logger log.Logger, opts Opts) *Server {
_ = level.Info(logger).Log("msg", fmt.Sprintf("not exporting user billing: %v", err))
}

muxIngress := http.NewServeMux()
httpServerIngress := &http.Server{
Handler: muxIngress,
ReadHeaderTimeout: 10 * time.Second,
}

workflowExporter := NewWorkflowMetricsExporter(logger, opts)
server := &Server{
logger: logger,
server: httpServer,
serverMetrics: httpServerMetrics,
serverIngress: httpServerIngress,
workflowMetricsExporter: workflowExporter,
billingExporter: billingExporter,
opts: opts,
}

mux.Handle(opts.MetricsPath, promhttp.Handler())
mux.HandleFunc(opts.WebhookPath, workflowExporter.HandleGHWebHook)
mux.HandleFunc("/", server.handleRoot)
muxMetrics.Handle(opts.MetricsPath, promhttp.Handler())
muxMetrics.HandleFunc(opts.WebhookPath, workflowExporter.HandleGHWebHook)

muxIngress.HandleFunc("/", server.handleRoot)

return server
}

func (s *Server) Serve(ctx context.Context) error {
listener, err := getListener(s.opts.ListenAddress, s.logger)
listenerMetrics, err := getListener(s.opts.ListenAddressMetrics, s.logger)
if err != nil {
return fmt.Errorf("get listener: %w", err)
}

listenerIgress, err := getListener(s.opts.ListenAddressIngress, s.logger)
if err != nil {
return fmt.Errorf("get listener: %w", err)
}

_ = level.Info(s.logger).Log("msg", "GitHub Actions Prometheus Exporter has successfully started")
err = s.server.Serve(listener)
_ = level.Info(s.logger).Log("msg", "GitHub Actions Prometheus Exporter Metrics has successfully started")
go func() {
_ = s.serverMetrics.Serve(listenerMetrics)
}()

_ = level.Info(s.logger).Log("msg", "GitHub Actions Prometheus Exporter Ingress has successfully started")
err = s.serverIngress.Serve(listenerIgress)
if err != nil && !errors.Is(err, http.ErrServerClosed) {
return fmt.Errorf("server closed: %w", err)
return fmt.Errorf("server ingress closed: %w", err)
}

return nil
}

func (s *Server) Shutdown(ctx context.Context) error {
return s.server.Shutdown(ctx)
err := s.serverMetrics.Shutdown(ctx)
if err != nil {
return err
}

err = s.serverIngress.Shutdown(ctx)
if err != nil {
return err
}

return nil
}

func (s *Server) handleRoot(w http.ResponseWriter, _ *http.Request) {
Expand All @@ -95,7 +124,6 @@ func (s *Server) handleRoot(w http.ResponseWriter, _ *http.Request) {
<body>
<h1>GitHub Actions Exporter</h1>
<p> ` + version.Print("ghactions_exporter") + ` </p>
<p><a href='` + s.opts.MetricsPath + `'>Metrics</a></p>
</body>
</html>
`))
Expand Down
34 changes: 26 additions & 8 deletions internal/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ import (
func Test_Server_MetricsRouteWithNoMetrics(t *testing.T) {
logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))
srv := server.NewServer(logger, server.Opts{
MetricsPath: "/metrics",
ListenAddress: ":8000",
WebhookPath: "/webhook",
GitHubToken: "webhook_token",
MetricsPath: "/metrics",
ListenAddressMetrics: ":8000",
ListenAddressIngress: ":8001",
WebhookPath: "/webhook",
GitHubToken: "webhook_token",
})

t.Cleanup(func() {
Expand All @@ -45,15 +46,32 @@ func Test_Server_MetricsRouteWithNoMetrics(t *testing.T) {
payload, err := io.ReadAll(res.Body)
require.NoError(t, err)
assert.NotNil(t, payload)

res, err = http.Get("http://localhost:8000")
require.NoError(t, err)
defer res.Body.Close()

assert.Equal(t, 404, res.StatusCode)

res, err = http.Get("http://localhost:8001")
require.NoError(t, err)
defer res.Body.Close()

assert.Equal(t, 200, res.StatusCode)

payload, err = io.ReadAll(res.Body)
require.NoError(t, err)
assert.NotNil(t, payload)
}

func Test_Server_MetricsRouteAfterWorkflowJob(t *testing.T) {
logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))
srv := server.NewServer(logger, server.Opts{
MetricsPath: "/metrics",
ListenAddress: ":8000",
WebhookPath: "/webhook",
GitHubToken: webhookSecret,
MetricsPath: "/metrics",
ListenAddressMetrics: ":8000",
ListenAddressIngress: ":8001",
WebhookPath: "/webhook",
GitHubToken: webhookSecret,
})

t.Cleanup(func() {
Expand Down
6 changes: 4 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import (
)

var (
listenAddress = kingpin.Flag("web.listen-address", "Address to listen on for web interface and telemetry.").Default(":9101").String()
listenAddressMetrics = kingpin.Flag("web.listen-address", "Address to listen on for metrics.").Default(":9101").String()
listenAddressIngress = kingpin.Flag("web.listen-address-ingress", "Address to listen on for web interface and receive webhook.").Default(":8065").String()
metricsPath = kingpin.Flag("web.telemetry-path", "Path under which to expose metrics.").Default("/metrics").String()
ghWebHookPath = kingpin.Flag("web.gh-webhook-path", "Path that will be called by the GitHub webhook.").Default("/gh_event").String()
githubWebhookToken = kingpin.Flag("gh.github-webhook-token", "GitHub Webhook Token.").Envar("GITHUB_WEBHOOK_TOKEN").Default("").String()
Expand Down Expand Up @@ -53,7 +54,8 @@ func main() {

srv := server.NewServer(logger, server.Opts{
WebhookPath: *ghWebHookPath,
ListenAddress: *listenAddress,
ListenAddressMetrics: *listenAddressMetrics,
ListenAddressIngress: *listenAddressIngress,
MetricsPath: *metricsPath,
GitHubToken: *githubWebhookToken,
GitHubAPIToken: *gitHubAPIToken,
Expand Down