File: kubernetes_container_resources_validator.rb

package info (click to toggle)
gitlab 17.6.5-19
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 629,368 kB
  • sloc: ruby: 1,915,304; javascript: 557,307; sql: 60,639; xml: 6,509; sh: 4,567; makefile: 1,239; python: 406
file content (77 lines) | stat: -rw-r--r-- 2,850 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# frozen_string_literal: true

# KubernetesPodContainerResourcesValidator
#
# Validates that value is a Kubernetes resource specifying cpu and memory.
#
# Example:
#
#   class Group < ActiveRecord::Base
#     validates :resource, presence: true, kubernetes_pod_container_resources: true
#   end

class KubernetesContainerResourcesValidator < ActiveModel::EachValidator # rubocop:disable Gitlab/NamespacedClass -- This is a globally shareable validator, but it's unclear what namespace it should belong in
  # https://kubernetes.io/docs/tasks/configure-pod-container/assign-cpu-resource/#cpu-units
  # The CPU resource is measured in CPU units. Fractional values are allowed. You can use the suffix m to mean milli.
  # (\d+m|\d+(\.\d*)?): Two alternatives separated by |:
  #   \d+m: Matches positive whole numbers followed by "m".
  #   \d+(\.\d*)?: Matches positive decimal numbers.
  CPU_UNITS = /^(\d+m|\d+(\.\d*)?)$/

  # https://kubernetes.io/docs/tasks/configure-pod-container/assign-memory-resource/#memory-units
  # The memory resource is measured in bytes. You can express memory as a plain integer or a fixed-point integer
  # with one of these suffixes: E, P, T, G, M, K, Ei, Pi, Ti, Gi, Mi, Ki.
  # \d+(\.\d*)?: Matches positive decimal numbers.
  # ([EPTGMK]|[EPTGMK][i])?: Optional suffix part, where:
  #   [EPTGMK]: Matches a single character from the set E, P, T, G, M, K.
  #   [EPTGMK]i: Matches characters from the set followed by an "i".
  MEMORY_UNITS = /^\d+(\.\d*)?([EPTGMK]|[EPTGMK]i)?$/

  def validate_each(record, attribute, value)
    unless value.is_a?(Hash)
      record.errors.add(attribute, _("must be a hash"))
      return
    end

    if value == {}
      record.errors.add(
        attribute,
        _("must be a hash containing 'cpu' and 'memory' attribute of type string")
      )
      return
    end

    cpu = value.deep_symbolize_keys.fetch(:cpu, nil)
    unless cpu.is_a?(String)
      record.errors.add(
        attribute,
        format(_("'cpu: %{cpu}' must be a string"), cpu: cpu)
      )
    end

    if cpu.is_a?(String) && !CPU_UNITS.match?(cpu)
      record.errors.add(
        attribute,
        format(_("'cpu: %{cpu}' must match the regex '%{cpu_regex}'"), cpu: cpu, cpu_regex: CPU_UNITS.source)
      )
    end

    memory = value.deep_symbolize_keys.fetch(:memory, nil)
    unless memory.is_a?(String)
      record.errors.add(
        attribute,
        format(_("'memory: %{memory}' must be a string"), memory: memory)
      )
    end

    if memory.is_a?(String) && !MEMORY_UNITS.match?(memory) # rubocop:disable Style/GuardClause -- Easier to read this way
      record.errors.add(
        attribute,
        format(_("'memory: %{memory}' must match the regex '%{memory_regex}'"),
          memory: memory,
          memory_regex: MEMORY_UNITS.source
        )
      )
    end
  end
end