Skip to content

Commit

Permalink
Add global metrics collectors
Browse files Browse the repository at this point in the history
This allows signon to provide prometheus metrics about the expiry time
for api user tokens. The `metrics` method is called everytime the
  `/metrics` endpoint is request (once a minute), hence we cache the
  expensive database call as tokens are unlikely to change frequently.

This metric collector is used by the prometheus exporter.

These token expiry timestamps are need to setup monitoring and alerting
to notify us when tokens need rotating.

Fixup: add test
  • Loading branch information
theseanything committed Sep 12, 2023
1 parent ce24711 commit f01542b
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 1 deletion.
4 changes: 3 additions & 1 deletion config/initializers/prometheus.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
require "govuk_app_config/govuk_prometheus_exporter"
GovukPrometheusExporter.configure
require "collectors/global_prometheus_collector"

GovukPrometheusExporter.configure(collectors: [Collectors::GlobalPrometheusCollector])
40 changes: 40 additions & 0 deletions lib/collectors/global_prometheus_collector.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require "prometheus_exporter"
require "prometheus_exporter/server"

# Load Rails models as not automatically included for Prometheus Exporter
require File.expand_path("../../config/environment", __dir__) unless defined? Rails

module Collectors
class GlobalPrometheusCollector < PrometheusExporter::Server::TypeCollector
def type
"signon_global"
end

def metrics
token_expiry_timestamp = PrometheusExporter::Metric::Gauge.new("signon_api_user_token_expiry_timestamp_seconds", "Timestamp when API User token expires")

token_expiry_info.each do |token|
token_expiry_timestamp.observe(token[:expires_at], api_user: token[:api_user], application: token[:application_name])
end

[token_expiry_timestamp]
end

private

def token_expiry_info
# Cache metric to prevent needless expensive calls to the database
Rails.cache.fetch("token_expiry_info", expires_in: 1.hour) do
ApiUser.all.flat_map do |user|
user.authorisations.where(revoked_at: nil).map do |token|
{
expires_at: token.expires_at.to_i,
api_user: user.email,
application_name: token.application.name.parameterize,
}
end
end
end
end
end
end
27 changes: 27 additions & 0 deletions test/lib/collectors/global_prometheus_collector_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
require "test_helper"

class GlobalPrometheusCollectorTest < ActiveSupport::TestCase
def setup
@collector = Collectors::GlobalPrometheusCollector.new
@api_user = api_user_with_token("user1", token_count: 3)
end

context "#metrics" do
should "list all non-revoked token expiry timestamps" do
@api_user.authorisations[2].revoke

metrics = @collector.metrics

assert_equal metrics.first.data, {
{
api_user: @api_user.email,
application: @api_user.authorisations.first.application.name.parameterize,
} => @api_user.authorisations.first.expires_at.to_i,
{
api_user: @api_user.email,
application: @api_user.authorisations.second.application.name.parameterize,
} => @api_user.authorisations.second.expires_at.to_i,
}
end
end
end

0 comments on commit f01542b

Please sign in to comment.