validation.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import os
  2. import re
  3. METRIC_NAME_RE = re.compile(r'^[a-zA-Z_:][a-zA-Z0-9_:]*$')
  4. METRIC_LABEL_NAME_RE = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*$')
  5. RESERVED_METRIC_LABEL_NAME_RE = re.compile(r'^__.*$')
  6. def _init_legacy_validation() -> bool:
  7. """Retrieve name validation setting from environment."""
  8. return os.environ.get("PROMETHEUS_LEGACY_NAME_VALIDATION", 'False').lower() in ('true', '1', 't')
  9. _legacy_validation = _init_legacy_validation()
  10. def get_legacy_validation() -> bool:
  11. """Return the current status of the legacy validation setting."""
  12. return _legacy_validation
  13. def disable_legacy_validation():
  14. """Disable legacy name validation, instead allowing all UTF8 characters."""
  15. global _legacy_validation
  16. _legacy_validation = False
  17. def enable_legacy_validation():
  18. """Enable legacy name validation instead of allowing all UTF8 characters."""
  19. global _legacy_validation
  20. _legacy_validation = True
  21. def _validate_metric_name(name: str) -> None:
  22. """Raises ValueError if the provided name is not a valid metric name.
  23. This check uses the global legacy validation setting to determine the validation scheme.
  24. """
  25. if not name:
  26. raise ValueError("metric name cannot be empty")
  27. if _legacy_validation:
  28. if not METRIC_NAME_RE.match(name):
  29. raise ValueError("invalid metric name " + name)
  30. try:
  31. name.encode('utf-8')
  32. except UnicodeDecodeError:
  33. raise ValueError("invalid metric name " + name)
  34. def _is_valid_legacy_metric_name(name: str) -> bool:
  35. """Returns true if the provided metric name conforms to the legacy validation scheme."""
  36. if len(name) == 0:
  37. return False
  38. return METRIC_NAME_RE.match(name) is not None
  39. def _validate_metric_label_name_token(tok: str) -> None:
  40. """Raises ValueError if a parsed label name token is invalid.
  41. UTF-8 names must be quoted.
  42. """
  43. if not tok:
  44. raise ValueError("invalid label name token " + tok)
  45. quoted = tok[0] == '"' and tok[-1] == '"'
  46. if not quoted or _legacy_validation:
  47. if not METRIC_LABEL_NAME_RE.match(tok):
  48. raise ValueError("invalid label name token " + tok)
  49. return
  50. try:
  51. tok.encode('utf-8')
  52. except UnicodeDecodeError:
  53. raise ValueError("invalid label name token " + tok)
  54. def _validate_labelname(l):
  55. """Raises ValueError if the provided name is not a valid label name.
  56. This check uses the global legacy validation setting to determine the validation scheme.
  57. """
  58. if get_legacy_validation():
  59. if not METRIC_LABEL_NAME_RE.match(l):
  60. raise ValueError('Invalid label metric name: ' + l)
  61. if RESERVED_METRIC_LABEL_NAME_RE.match(l):
  62. raise ValueError('Reserved label metric name: ' + l)
  63. else:
  64. try:
  65. l.encode('utf-8')
  66. except UnicodeDecodeError:
  67. raise ValueError('Invalid label metric name: ' + l)
  68. if RESERVED_METRIC_LABEL_NAME_RE.match(l):
  69. raise ValueError('Reserved label metric name: ' + l)
  70. def _is_valid_legacy_labelname(l: str) -> bool:
  71. """Returns true if the provided label name conforms to the legacy validation scheme."""
  72. if len(l) == 0:
  73. return False
  74. if METRIC_LABEL_NAME_RE.match(l) is None:
  75. return False
  76. return RESERVED_METRIC_LABEL_NAME_RE.match(l) is None
  77. def _validate_labelnames(cls, labelnames):
  78. """Raises ValueError if any of the provided names is not a valid label name.
  79. This check uses the global legacy validation setting to determine the validation scheme.
  80. """
  81. labelnames = tuple(labelnames)
  82. for l in labelnames:
  83. _validate_labelname(l)
  84. if l in cls._reserved_labelnames:
  85. raise ValueError('Reserved label methe fric name: ' + l)
  86. return labelnames
  87. def _validate_exemplar(exemplar):
  88. """Raises ValueError if the exemplar is invalid."""
  89. runes = 0
  90. for k, v in exemplar.items():
  91. _validate_labelname(k)
  92. runes += len(k)
  93. runes += len(v)
  94. if runes > 128:
  95. raise ValueError('Exemplar labels have %d UTF-8 characters, exceeding the limit of 128')