Каждый, кто работал с Helm чартами, сталкивался с необходимостью валидации values.yaml. Стандартный подход с использованием JSON Schema работает, но часто становится громоздким и ограниченным. В этой статье я хочу представить Helm CEL — плагин, который позволяет использовать Common Expression Language (CEL) от Google для валидации конфигурации Helm чартов.

Что такое CEL и почему это важно?

Common Expression Language (CEL) — это простой и мощный язык выражений, разработанный Google. Он активно используется в экосистеме Kubernetes, например, для валидации CRD и конфигурации Istio. CEL предоставляет простой, но выразительный синтаксис для описания правил валидации.

Руководство для начинающих по CEL

Основные преимущества CEL:

  • Типобезопасность

  • Детерминированное выполнение

  • Простой и понятный синтаксис

  • Поддержка сложных условий и выражений

  • Встроенные функции для работы со строками, числами и коллекциями

Установка и базовое использование

Установка плагина выполняется одной командой:

helm plugin install https://github.com/idsulik/helm-cel

Вместо создания values.schema.json, мы создаем файл values.cel.yaml в директории чарта. Пример простой конфигурации:

rules:
  - expr: "has(values.service) && has(values.service.port)"
    desc: "Необходимо указать port в конфигурации service"
  
  - expr: "values.service.port >= 1 && values.service.port <= 65535"
    desc: "Port должен быть в диапазоне от 1 до 65535"
  
  - expr: "!(has(values.replicaCount)) || values.replicaCount >= 1"
    desc: "Если указан replicaCount, он должен быть больше или равен 1"

Запуск валидации:

helm cel ./mychart

Практические примеры

Давайте рассмотрим несколько реальных сценариев валидации и сравним их реализацию в JSON Schema и CEL.

1. Проверка обязательных полей

JSON Schema:

{
  "required": ["service"],
  "properties": {
    "service": {
      "required": ["port", "type"],
      "properties": {
        "port": {
          "type": "integer"
        },
        "type": {
          "type": "string",
          "enum": ["ClusterIP", "NodePort", "LoadBalancer"]
        }
      }
    }
  }
}

CEL:

rules:
  - expr: "has(values.service) && has(values.service.port) && has(values.service.type)"
    desc: "service должен содержать port и type"
  
  - expr: has(values.service.type) && values.service.type in ['ClusterIP', 'NodePort', 'LoadBalancer']
    desc: "type должен быть одним из: ClusterIP, NodePort, LoadBalancer"

2. Условная валидация

Например, если включено persistence, требуется указать storageClass:

JSON Schema:

{
  "if": {
    "properties": {
      "persistence": {
        "const": true
      }
    }
  },
  "then": {
    "required": ["storageClass"],
    "properties": {
      "storageClass": {
        "type": "string",
        "minLength": 1
      }
    }
  }
}

CEL:

rules:
  - expr: |
      !has(values.persistence) || 
      !values.persistence || 
      (has(values.persistence.storageClass) && size(values.persistence.storageClass) > 0)
    desc: "При включенном persistence необходимо указать storageClass"

Информативные сообщения об ошибках

Одно из главных преимуществ Helm CEL — понятные сообщения об ошибках:

❌ Validation failed: Запрошенная память не может превышать лимит
   Rule: values.resources.requests.memory <= values.resources.limits.memory
   Path: resources.requests.memory
   Current value: "2Gi" (limits: "1Gi")

Производительность

CEL-выражения компилируются перед выполнением, что обеспечивает высокую производительность. Плагин добавляет минимальные накладные расходы к процессу работы с Helm, что делает его подходящим как для разработки, так и для CI/CD пайплайнов.

Расширенные возможности

Работа с типами данных

CEL предоставляет богатый набор функций для работы с различными типами данных:

rules:
  # Проверка строк
  - expr: "has(values.name) && size(values.name) >= 3 && size(values.name) <= 63"
    desc: "Имя должно быть от 3 до 63 символов"

  # Работа с числами
  - expr: "type(values.timeout) == int && values.timeout > 0"
    desc: "Timeout должен быть положительным числом"

  # Проверка списков
  - expr: |
      !has(values.labels) || 
      values.labels.all(k, k.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'))
    desc: "Labels должны соответствовать формату Kubernetes"

Комбинирование правил

rules:
  - expr: |
      !has(values.autoscaling) ||
      !values.autoscaling.enabled ||
      (has(values.autoscaling.minReplicas) &&
       has(values.autoscaling.maxReplicas) &&
       has(values.autoscaling.targetCPUUtilizationPercentage) &&
       values.autoscaling.minReplicas <= values.autoscaling.maxReplicas &&
       values.autoscaling.targetCPUUtilizationPercentage > 0 &&
       values.autoscaling.targetCPUUtilizationPercentage <= 100)
    desc: "Некорректная конфигурация автомасштабирования"

Интеграция с CI/CD

Пример использования в GitLab CI:

validate_helm:
  image: alpine/helm:3.9.0
  script:
    - helm plugin install https://github.com/idsulik/helm-cel
    - helm cel ./chart
  rules:
    - changes:
        - chart/**/*

Заключение

Helm CEL предоставляет мощный и гибкий способ валидации Helm чартов. По сравнению с JSON Schema, он позволяет писать более понятные и поддерживаемые правила валидации. Комбинация знакомого синтаксиса, выразительных возможностей и четких сообщений об ошибках делает его отличным инструментом для работы с Helm чартами.

Просьба поставить ? для репозитория, если вы думаете, что плагин полезный.

Полезные ссылки

  1. GitHub репозиторий проекта

  2. Документация по CEL

  3. Спецификация CEL

Комментарии (5)


  1. Cykooz
    17.11.2024 06:48

    Одно из главных преимуществ Helm CEL — понятные сообщения об ошибках

    И один из главных недостатков, т.к. текст ошибки должен полностью написать человек самостоятельно. И поддерживать его актуальность в будущем, что может быть не совсем просто из-за человеческого фактора.

    А что насчёт переводов текстов ошибок на разные языки?

    Может там есть режим автоматической генерации описания ошибки, на основе выражения?


    1. idsulik Автор
      17.11.2024 06:48

      можно опустить описание, тогда будет ошибка без описания:

      Насчет автогенерации описания, хорошая идея, но это наверно подойдет для простых выражений, а для сложных выйдет большое описание, что наверное так себе.

      Постараюсь реализовать, если у кого есть желание, то welcome в pull requests)


  1. linoles
    17.11.2024 06:48

    Отличная статья о Helm CEL! Я впечатлён тем, как вы объяснили преимущества использования CEL для валидации Helm чартов. Примеры, приведенные в сравнении с JSON Schema, действительно помогают понять, насколько более гибким и читаемым является подход с CEL. Особенно полезно было увидеть применение условной валидации и информативные сообщения об ошибках. Это значительно упрощает процесс отладки. Было бы интересно узнать больше о возможностях интеграции Helm CEL с другими инструментами в CI/CD пайплайнах. Спасибо за ценный материал!


    1. idsulik Автор
      17.11.2024 06:48

      Спасибо! Какие именно еще возможности интересуют?


      1. shurup
        17.11.2024 06:48

        К сожалению, я уверен, что сообщение выше написал ChatGPT.