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

Validate naming #310

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions lib/prometheus_exporter/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

module PrometheusExporter
class Client
NAMING_VALIDATION_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/

class RemoteMetric
attr_reader :name, :type, :help

Expand Down Expand Up @@ -128,6 +130,8 @@ def find_registered_metric(name, type: nil, help: nil)
end

def send_json(obj)
return false unless valid_naming?(obj)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we raise instead? Otherwise the error would be pretty silent.

Also I am wondering if could be done somewhere else to avoid extra check on every send_json. For example when the metric is setup?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My initial thought was to add it in RemoteMetric as such

class RemoteMetric
  def valid?
     # ...
   end
end

But that doesn't cover the custom labels passed to the instance methods of RemoteMetric nor the global custom_labels.

Where were you thinking it would be a nice place?

About the error, I'm not sure 🤔 My initial thoughts were:

  • Let's say you are using something like Sentry. You can flood your account with error events quite easily.
  • Is it better to have an app crashing in the middle because of metrics?

But I'm up for discussing this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And lastly, one can call send_json directly I think so the MR was safeguarding against that as well.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct. I think your change is the way to go.


payload =
if @custom_labels
if obj[:custom_labels]
Expand Down Expand Up @@ -269,6 +273,28 @@ def wait_for_empty_queue_with_timeout(timeout_seconds)
sleep(0.05)
end
end

def valid_naming?(obj)
if obj[:name]
return false unless obj[:name].match?(NAMING_VALIDATION_REGEX)
end

custom_label_names = []
if obj[:custom_labels]
custom_label_names.concat(obj[:custom_labels].keys)
end

if @custom_labels
custom_label_names.concat(@custom_labels.keys)
end

return true if custom_label_names.empty?

custom_label_names.all? { |name|
str = name.to_s
str.match?(NAMING_VALIDATION_REGEX) && !str.start_with?("__")
}
end
end

class LocalClient < Client
Expand Down
21 changes: 21 additions & 0 deletions test/client_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,25 @@ def test_overriding_logger

assert_includes(logs.string, "dropping message cause queue is full")
end

def test_invalid_naming
client = PrometheusExporter::Client.new(custom_labels: {"label" => 1})

refute_same(false, client.send_json({}))
refute_same(false, client.send_json({name: "valid_name"}))
refute_same(false, client.send_json({name: "__valid_name"}))
refute_same(false, client.send_json({name: "__valid_name_1"}))
refute_same(false, client.send_json({name: "__valid_name_1", custom_labels: {"valid_name" => "value"}}))
refute_same(false, client.send_json({name: "valid_name", custom_labels: {"_valid_name" => "value"}}))

assert_equal(false, client.send_json({name: "invalid-name"}))
assert_equal(false, client.send_json({name: "valid_name", custom_labels: {"__invalid_name" => "value"}}))
assert_equal(false, client.send_json({name: "valid_name", custom_labels: {"invalid-name" => "value"}}))

client.custom_labels = {"__invalid_name" => "value"}
assert_equal(false, client.send_json({}))

client.custom_labels = {"invalid-name" => "value"}
assert_equal(false, client.send_json({}))
end
end