Загальна мова виразів у Kubernetes

Загальна мова виразів (Common Expression Language, CEL) використовується в API Kubernetes для оголошення правил валідації, політик та інших обмежень чи умов.

Вирази CEL оцінюються безпосередньо на сервері API, що робить CEL зручним альтернативним рішенням для зовнішніх механізмів, таких як вебхуки, для багатьох випадків розширення функціональності. Ваші вирази CEL продовжують виконуватись, поки компонент сервера API у складі панелі управління залишається доступним.

Огляд мови

Мова CEL має простий синтаксис, який схожий на вирази в C, C++, Java, JavaScript і Go.

CEL була розроблена для вбудовування в застосунки. Кожна "програма" CEL — це один вираз, який обраховується до одного значення. Вирази CEL зазвичай короткі "одноразові", що добре вбудовуються в строкові поля ресурсів API Kubernetes.

Вхідні дані для програми CEL — це "змінні". Кожне поле API Kubernetes, яке містить CEL, декларує в документації API, які змінні доступні для використання для цього поля. Наприклад, у полі x-kubernetes-validations[i].rules у CustomResourceDefinitions доступні змінні self і oldSelf, що відносяться до попереднього та поточного стану даних власного ресурсу користувача, які потрібно перевірити за допомогою виразу CEL. Інші поля API Kubernetes можуть оголошувати різні змінні. Дивіться документацію API для полів API, щоб дізнатися, які змінні доступні для цього поля.

Приклади виразів CEL:

Приклади виразів CEL та їх призначення
ПравилоПризначення
self.minReplicas <= self.replicas && self.replicas <= self.maxReplicasПеревірити, що три поля, що визначають репліки, розташовані в правильному порядку
'Available' in self.stateCountsПеревірити, що в map існує запис з ключем 'Available'
(self.list1.size() == 0) != (self.list2.size() == 0)Перевірити, що один із двох списків не порожній, але не обидва одночасно
self.envars.filter(e, e.name = 'MY_ENV').all(e, e.value.matches('^[a-zA-Z]*$'))Перевірити поле 'value' у запису listMap, де поле ключа 'name' дорівнює 'MY_ENV'
has(self.expired) && self.created + self.ttl < self.expiredПеревірити, що дата 'expired' є пізніше дати 'created' плюс тривалість 'ttl'
self.health.startsWith('ok')Перевірити, що строкове поле 'health' має префікс 'ok'
self.widgets.exists(w, w.key == 'x' && w.foo < 10)Перевірити, що властивість 'foo' елемента listMap з ключем 'x' менше 10
type(self) == string ? self == '99%' : self == 42Перевірити поле типу int-або-string для обох випадків: int та string
self.metadata.name == 'singleton'Перевірити, що імʼя обʼєкта відповідає конкретному значенню (робить його унікальним)
self.set1.all(e, !(e in self.set2))Перевірити, що два списки (listSets) не перетинаються
self.names.size() == self.details.size() && self.names.all(n, n in self.details)Перевірити, що map 'details' має ключі, які відповідають елементам у списку 'names'
self.details.all(key, key.matches('^[a-zA-Z]*$'))Перевірити ключі map 'details'
self.details.all(key, self.details[key].matches('^[a-zA-Z]*$'))Перевірити значення map 'details'

Опції CEL, особливості мови та бібліотеки

CEL налаштовується з наступними опціями, бібліотеками та особливостями мови, введеними у зазначених версіях Kubernetes:

Опція, бібліотека або особливість мови CELВключеноДоступність
Стандартні макросиhas, all, exists, exists_one, map, filterУсі версії Kubernetes
Стандартні функціїДивіться офіційний список стандартних визначеньУсі версії Kubernetes
Однорідні агрегаційні літералиУсі версії Kubernetes
Часовий пояс UTC за замовчуваннямУсі версії Kubernetes
Рання перевірка деклараційУсі версії Kubernetes
Бібліотека розширених рядків, Версія 1charAt, indexOf, lastIndexOf, lowerAscii, upperAscii, replace, split, join, substring, trimВерсії Kubernetes між 1.25 та 1.30
Бібліотека розширених рядків, Версія 2charAt, indexOf, lastIndexOf, lowerAscii, upperAscii, replace, split, join, substring, trimВерсії Kubernetes 1.30+
Бібліотека списків KubernetesДивіться бібліотеку списків KubernetesУсі версії Kubernetes
Бібліотека регулярних виразів KubernetesДивіться бібліотеку регулярних виразів KubernetesУсі версії Kubernetes
Бібліотека URL KubernetesДивіться бібліотеку URL KubernetesУсі версії Kubernetes
Бібліотека IP адрес KubernetesДивіться бібліотеку IP адрес KubernetesВерсії Kubernetes 1.31+
Бібліотека CIDR KubernetesДивіться бібліотеку CIDR KubernetesВерсії Kubernetes 1.31+
Бібліотека авторизації KubernetesДивіться бібліотеку авторизації KubernetesУсі версії Kubernetes
Бібліотека кількостей KubernetesДивіться бібліотеку кількостей KubernetesВерсії Kubernetes 1.29+
Бібліотека semver KubernetesДивіться бібліотеку semver KubernetesВерсії Kubernetes 1.34+
Бібліотека форматів KubernetesДивіться бібліотеку форматів KubernetesВерсії Kubernetes 1.32+
Опційні типи CELДивіться опційні типи CELВерсії Kubernetes 1.29+
CEL CrossTypeNumericComparisonsДивіться CEL CrossTypeNumericComparisonsВерсії Kubernetes 1.29+
CEL TwoVarComprehensionsДивіться CEL TwoVarComprehensionsВерсії Kubernetes 1.33+

Функції CEL, особливості та налаштування мови підтримують відкат панелі управління Kubernetes. Наприклад, опційні значення CEL були введені у Kubernetes 1.29, і лише сервери API цієї версії або новіші прийматимуть запити на запис виразів CEL, які використовують опційні значення CEL. Однак, коли кластер відкочується до версії Kubernetes 1.28, вирази CEL, що використовують "опційні значення CEL", які вже збережені в ресурсах API, продовжуватимуть правильно оцінюватись.

Бібліотеки CEL Kubernetes

Крім спільнотних бібліотек CEL, Kubernetes включає бібліотеки CEL, які доступні у всіх місцях використання CEL в Kubernetes.

Бібліотека списків Kubernetes

Бібліотека списків включає indexOf та lastIndexOf, які працюють аналогічно функціям рядків з такими самими назвами. Ці функції повертають перший або останній індекс елемента у списку.

Бібліотека списків також включає min, max та sum. Сума підтримується для всіх типів чисел, а також для типу тривалості. Мінімум та максимум підтримуються для всіх типів, що можна порівняти.

Також надається функція isSorted для зручності та підтримується для всіх типів, які можна порівняти.

Приклади:

Приклади виразів CEL, що використовують функції бібліотеки списків
Вираз CELПризначення
names.isSorted()Перевірити, що список імен зберігається в алфавітному порядку
items.map(x, x.weight).sum() == 1.0Перевірити, що "ваги" списку обʼєктів дорівнюють 1.0
lowPriorities.map(x, x.priority).max() < highPriorities.map(x, x.priority).min()Перевірити, що два набори пріоритетів не перекриваються
names.indexOf('should-be-first') == 1Вимагати, щоб перше імʼя у списку було певним значенням

Для отримання додаткової інформації дивіться бібліотеку списків Kubernetes godoc.

Бібліотека регулярних виразів Kubernetes

Крім функції matches, наданої стандартною бібліотекою CEL, бібліотека регулярних виразів надає функції find та findAll, що дозволяють виконувати ширший спектр операцій з регулярними виразами.

Приклади:

Приклади виразів CEL, що використовують функції бібліотеки регулярних виразів
Вираз CELПризначення
"abc 123".find('[0-9]+')Знайти перше число у рядку
"1, 2, 3, 4".findAll('[0-9]+').map(x, int(x)).sum() < 100Перевірити, що сума чисел у рядку менше 100

Для отримання додаткової інформації дивіться бібліотеку регулярних виразів Kubernetes godoc.

Бібліотека URL Kubernetes

Для спрощення та безпечної обробки URL надані наступні функції:

  • isURL(string) перевіряє, чи є рядок рядком дійсним URL з пакетом Go net/url. Рядок повинен бути абсолютним URL.
  • url(string) URL конвертує рядок в URL або викликає помилку, якщо рядок не є дійсним URL.

Після розбору за допомогою функції url, отриманий обʼєкт URL має наступні методи доступу: getScheme, getHost, getHostname, getPort, getEscapedPath та getQuery.

Приклади:

Приклади виразів CEL, що використовують функції бібліотеки URL
Вираз CELПризначення
url('https://example.com:80/').getHost()Отримати частину хосту 'example.com:80' URL
url('https://example.com/path with spaces/').getEscapedPath()Повертає '/path%20with%20spaces/'

Для отримання додаткової інформації дивіться бібліотеку URL Kubernetes godoc.

Бібліотека IP адрес Kubernetes

Щоб зробити обробку IP-адрес простішою і безпечнішою, були додані наступні функції:

  • isIP(string) перевіряє, чи є рядок дійсною IP-адресою.
  • ip(string) IP конвертує рядок в обʼєкт IP-адреси або викликає помилку, якщо рядок не є дійсною IP-адресою.

Для обох функцій IP-адреса повинна бути адресою IPv4 або IPv6. Зіставлені з IPv4 IPv6-адреси (наприклад, ::ffff:1.2.3.4) не допускаються. IP-адреси з зонами (наприклад, fe80::1%eth0) не допускаються. Початкові нулі в октетах IPv4-адрес не допускаються.

Після розбору за допомогою функції ip отриманий обʼєкт IP має наступну бібліотеку функцій-членів:

Доступні функції-члени обʼєкта IP-адреси

Функція-членТип значення (CEL)Опис
isCanonical()boolПовертає true, якщо IP-адреса знаходиться в канонічній формі. Для кожної IP-адреси існує лише одна канонічна форма, тому поля, що містять IP-адреси в канонічній формі, можна розглядати як рядки при перевірці на рівність або унікальність.
family()intПовертає сімейство IP-адреси: 4 для IPv4 та 6 для IPv6.
isUnspecified()boolПовертає true, якщо IP-адреса є невизначеною адресою. Це може бути IPv4-адреса "0.0.0.0" або IPv6-адреса "::".
isLoopback()boolПовертає true, якщо IP-адреса є loopback адресою. Це може бути IPv4-адреса зі значенням 127.x.x.x або IPv6-адреса зі значенням ::1.
isLinkLocalMulticast()boolПовертає true, якщо IP-адреса є локальною груповою адресою каналу (link-local multicast). Це може бути IPv4-адреса зі значенням 224.0.0.x або IPv6-адреса в мережі ff00::/8.
isLinkLocalUnicast()boolПовертає true, якщо IP-адреса є локальною індивідуальною адресою каналу (link-local unicast). Це може бути IPv4-адреса зі значенням 169.254.x.x або IPv6-адреса в мережі fe80::/10.
isGlobalUnicast()boolПовертає true, якщо IP-адреса є глобальною індивідуальною адресою. Це може бути IPv4-адреса, що не є нулем або 255.255.255.255, або IPv6-адреса, що не є локальною індивідуальною адресою каналу, зворотною адресою або груповою адресою.

Приклади:

Приклади виразів CEL з використанням функцій бібліотеки IP-адрес

Вираз CELПризначення
isIP('127.0.0.1')Повертає true для дійсної IP-адреси.
ip('2001:db8::abcd').isCanonical()Повертає true для канонічної IPv6-адреси.
ip('2001:DB8::ABCD').isCanonical()Повертає false, оскільки канонічна форма використовує нижній регістр.
ip('127.0.0.1').family() == 4Перевіряє сімейство IP-адреси.
ip('::1').isLoopback()Перевіряє, чи є IP-адреса зворотною (loopback).
ip('192.168.0.1').isGlobalUnicast()Перевіряє, чи є IP-адреса глобальною індивідуальною адресою.

Докладнішу інформацію наведено у Бібліотеці IP-адрес Kubernetes godoc.

Бібліотека CIDR Kubernetes

CIDR надає розширення бібліотеки функцій CEL для розбору нотації CIDR.

cidr

Конвертує рядок у нотації CIDR в представлення мережевої адреси або викликає помилку, якщо рядок не є дійсною нотацією CIDR. CIDR повинен бути підмережею IPv4 або IPv6 з маскою. Ведучі нулі в октетах IPv4-адреси не дозволяються. IPv4-адреси, що відображаються в IPv6 (наприклад, ::ffff:1.2.3.4/24), не дозволяються.

cidr(<рядок>) <CIDR>

Приклади:

cidr('192.168.0.0/16') // повертає IPv4-адресу з маскою CIDR
cidr('::1/128')        // повертає IPv6-адресу з маскою CIDR
cidr('192.168.0.0/33') // помилка
cidr('::1/129')        // помилка
cidr('192.168.0.1/16') // помилка, тому що є ненульові біти після префікса

isCIDR

Повертає true, якщо рядок є дійсним представленням підмережі з маскою в нотації CIDR. CIDR повинен бути підмережею IPv4 або IPv6 з маскою. Ведучі нулі в октетах IPv4-адреси не дозволяються. IPv4-адреси, що відображаються в IPv6 (наприклад, ::ffff:1.2.3.4/24), не дозволяються.

isCIDR(<string>) <bool>

Приклади:

isCIDR('192.168.0.0/16')    // повертає true
isCIDR('::1/128')           // повертає true
isCIDR('192.168.0.0/33')    // повертає false
isCIDR('::1/129')           // повертає false

containsIP / containsCIDR / ip / masked / prefixLength

  • containsIP: Повертає true, якщо CIDR містить задану IP-адресу. IP-адреса має бути IPv4 або IPv6. Може приймати як аргумент рядок або IP-адресу.

  • containsCIDR: Повертає true, якщо CIDR містить заданий CIDR. CIDR повинен бути підмережею IPv4 або IPv6 з маскою. Може приймати як аргумент рядок або CIDR.

  • ip: Повертає представлення IP-адреси CIDR.

  • masked: Повертає представлення CIDR мережевої адреси з маскованим префіксом. Це можна використовувати для повернення канонічної форми мережі CIDR.

  • prefixLength: Повертає довжину префікса CIDR в бітах. Це кількість бітів у масці.

Приклади:

Приклади виразів CEL з використанням функцій бібліотеки CIDR

Вираз CELПризначення
cidr('192.168.0.0/24').containsIP(ip('192.168.0.1'))Перевіряє, чи містить CIDR вказану IP-адресу (обʼєкт IP).
cidr('192.168.0.0/24').containsIP(ip('192.168.1.1'))Перевіряє, чи містить CIDR вказану IP-адресу (обʼєкт IP).
cidr('192.168.0.0/24').containsIP('192.168.0.1')Перевіряє, чи містить CIDR вказану IP-адресу (рядок).
cidr('192.168.0.0/24').containsIP('192.168.1.1')Перевіряє, чи містить CIDR вказану IP-адресу (рядок).
cidr('192.168.0.0/16').containsCIDR(cidr('192.168.10.0/24'))Перевіряє, чи містить CIDR інший вказаний CIDR (обʼєкт CIDR).
cidr('192.168.1.0/24').containsCIDR(cidr('192.168.2.0/24'))Перевіряє, чи містить CIDR інший вказаний CIDR (обʼєкт CIDR).
cidr('192.168.0.0/16').containsCIDR('192.168.10.0/24')Перевіряє, чи містить CIDR інший вказаний CIDR (рядок).
cidr('192.168.1.0/24').containsCIDR('192.168.2.0/24')Перевіряє, чи містить CIDR інший вказаний CIDR (рядок).
cidr('192.168.0.1/24').ip()Повертає частину IP-адреси CIDR.
cidr('192.168.0.1/24').ip().family()Повертає сімейство частини IP-адреси CIDR.
cidr('::1/128').ip()Повертає частину IP-адреси IPv6 CIDR.
cidr('::1/128').ip().family()Повертає сімейство частини IP-адреси IPv6 CIDR.
cidr('192.168.0.0/24').masked()Повертає канонічну форму мережі CIDR.
cidr('192.168.0.1/24').masked()Повертає канонічну форму мережі CIDR, маскуючи біти після префікса.
cidr('192.168.0.0/24') == cidr('192.168.0.0/24').masked()Порівнює CIDR з його канонічною формою (вже канонічний).
cidr('192.168.0.1/24') == cidr('192.168.0.1/24').masked()Порівнює CIDR з його канонічною формою (не канонічний).
cidr('192.168.0.0/16').prefixLength()Повертає довжину префікса IPv4 CIDR.
cidr('::1/128').prefixLength()Повертає довжину префікса IPv6 CIDR.

Докладнішу інформацію можна знайти у Бібліотеці Kubernetes CIDR godoc.

Бібліотека авторизатора Kubernetes

Для виразів CEL в API, де доступна змінна типу Authorizer, авторизатор може використовуватися для виконання перевірок авторизації для принципала (автентифікованого користувача) запиту.

Перевірки ресурсів API виконуються наступним чином:

  1. Вкажіть групу та ресурс для перевірки: Authorizer.group(string).resource(string) ResourceCheck.

  2. Опційно викличте будь-яку комбінацію наступних функцій побудови для подальшого обмеження перевірки авторизації. Зверніть увагу, що ці функції повертають тип отримувача та можуть бути зʼєднані ланцюгом:

    • ResourceCheck.subresource(string) ResourceCheck
    • ResourceCheck.namespace(string) ResourceCheck
    • ResourceCheck.name(string) ResourceCheck
  3. Викличте ResourceCheck.check(verb string) Decision, щоб виконати перевірку авторизації.

  4. Викличте allowed() bool або reason() string, щоб переглянути результат перевірки авторизації.

Не-ресурсна авторизація виконується так:

  1. Вкажіть лише шлях: Authorizer.path(string) PathCheck.
  2. Викличте PathCheck.check(httpVerb string) Decision, щоб виконати перевірку авторизації.
  3. Викличте allowed() bool або reason() string, щоб переглянути результат перевірки авторизації.

Для виконання перевірки авторизації для службового облікового запису:

  • Authorizer.serviceAccount(namespace string, name string) Authorizer
Приклади виразів CEL, що використовують функції бібліотеки авторизатора
Вираз CELПризначення
authorizer.group('').resource('pods').namespace('default').check('create').allowed()Повертає true, якщо принципалу (користувачу або службовому обліковому запису) дозволено створювати Podʼи у просторі імен 'default'.
authorizer.path('/healthz').check('get').allowed()Перевіряє, чи авторизований принципал (користувач або службовий обліковий запис) виконує HTTP GET-запити до шляху API /healthz.
authorizer.serviceAccount('default', 'myserviceaccount').resource('deployments').check('delete').allowed()Перевіряє, чи службовий обліковий запис має дозвіл на видалення deployments.
СТАН ФУНКЦІОНАЛУ: Kubernetes v1.31 [alpha]

З увімкненою альфа-функцією AuthorizeWithSelectors, до перевірок авторизації можна додавати селектори полів і міток.

Приклади CEL виразів із використанням функцій авторизації для селекторів
CEL виразПризначення
authorizer.group('').resource('pods').fieldSelector('spec.nodeName=mynode').check('list').allowed()Повертає true, якщо користувач або службовий обліковий запис має дозвіл на отримання списку Podʼів із селектором полів spec.nodeName=mynode.
authorizer.group('').resource('pods').labelSelector('example.com/mylabel=myvalue').check('list').allowed()Повертає true, якщо користувач або службовий обліковий запис має дозвіл на отримання списку Podʼів із селектором міток example.com/mylabel=myvalue.

Для отримання додаткової інформації дивіться бібліотеку Kubernetes Authz та бібліотеку Kubernetes AuthzSelectors godoc.

Бібліотека форматів Kubernetes

Бібліотека format надає функції для перевірки поширених форматів рядків у Kubernetes. Це корисно у messageExpression правил валідації для надання більш конкретних повідомлень про помилки.

Бібліотека надає функції format() для кожного іменованого формату, а також загальну функцію format.named().

  • format.named(string)?Format: Повертає обʼєкт Format для заданого імені формату, якщо він існує. Інакше повертає optional.none.
  • format.<formatName>() -> Format: Доступні зручні функції для всіх іменованих форматів. Наприклад, format.dns1123Label() повертає обʼєкт Format для DNS-1123 label.
  • <Format>.validate(string) -> list<string>?: Перевіряє рядок на відповідність формату. Повертає optional.none, якщо рядок валідний, інакше — список рядків з помилками.

Доступні формати:

Підтримуються наступні назви форматів:

Доступні формати для бібліотеки форматів

Назва форматуОпис
dns1123LabelПеревіряє, чи є рядок валідною DNS-1123 міткою.
dns1123SubdomainПеревіряє, чи є рядок валідним DNS-1123 піддоменом.
dns1035LabelПеревіряє, чи є рядок валідною DNS-1035 міткою.
qualifiedNameПеревіряє, чи є рядок валідним кваліфікованим іменем.
dns1123LabelPrefixПеревіряє, чи є рядок валідним префіксом DNS-1123 мітки.
dns1123SubdomainPrefixПеревіряє, чи є рядок валідним префіксом DNS-1123 піддомену.
dns1035LabelPrefixПеревіряє, чи є рядок валідним префіксом DNS-1035 мітки.
labelValueПеревіряє, чи є рядок валідним значенням мітки.
uriПеревіряє, чи є рядок валідним URI. Використовує той самий патерн, що і isURL, але повертає список помилок.
uuidПеревіряє, чи є рядок валідним UUID.
byteПеревіряє, чи є рядок валідним base64-кодованим рядком.
dateПеревіряє, чи є рядок валідною датою у форматі YYYY-MM-DD.
datetimeПеревіряє, чи є рядок валідною датою-часом у форматі RFC3339.

Приклади:

Приклади CEL-виразів з використанням функцій бібліотеки форматів

Вираз CELПризначення
!format.dns1123Label().validate(self.metadata.name).hasValue()Правило валідації, що перевіряє, чи імʼя обʼєкта є валідною DNS-1123 міткою.
format.dns1123Label().validate(self.metadata.name).orValue([]).join("\n")messageExpression, що повертає конкретні помилки валідації для поля. Якщо поле валідне, validate повертає optional.none, а orValue — порожній список, результатом буде порожній рядок.

Докладніше: Бібліотека форматів Kubernetes godoc.

Бібліотека кількості Kubernetes

У Kubernetes 1.28 додана підтримка обробки рядків кількості (наприклад, 1,5G, 512k, 20Mi).

  • isQuantity(string) перевіряє, чи є рядок дійсною кількістю відповідно до Кількості ресурсів Kubernetes.
  • quantity(string) Quantity конвертує рядок у кількість або викликає помилку, якщо рядок не є дійсною кількістю.

Після розбору за допомогою функції quantity, отриманий обʼєкт кількості має наступний набір методів:

Набір доступних методів для кількості
МетодПовертає типОпис
isInteger()boolПовертає true, якщо asInteger може бути викликано без помилки.
asInteger()intПовертає представлення поточного значення як int64, якщо це можливо, або викликає помилку, якщо конвертація призводить до переповнення або втрати точності.
asApproximateFloat()floatПовертає представлення кількості як float64, що може втратити точність.
sign()intПовертає 1, якщо кількість додатня, -1, якщо вона відʼємна, або 0, якщо вона нуль.
add(<Quantity>)QuantityПовертає суму двох кількостей.
add(<int>)QuantityПовертає суму кількості та цілого числа.
sub(<Quantity>)QuantityПовертає різницю між двома кількостями.
sub(<int>)QuantityПовертає різницю між кількістю та цілим числом.
isLessThan(<Quantity>)boolПовертає true, якщо отримувач менше операнта.
isGreaterThan(<Quantity>)boolПовертає true, якщо отримувач більше операнта.
compareTo(<Quantity>)intПорівнює отримувача з операндом та повертає 0, якщо вони рівні, 1, якщо отримувач більший або -1, якщо отримувач менший за операнд.

Приклади:

Приклади виразів CEL, що використовують функції бібліотеки кількості
Вираз CELПризначення
quantity("500000G").isInteger()Перевірка, чи конвертація в ціле число викликає помилку.
quantity("50k").asInteger()Точна конвертація в ціле число.
quantity("9999999999999999999999999999999999999G").asApproximateFloat()Втратна конвертація в плаваючий рядок.
quantity("50k").add(quantity("20k"))Додати дві кількості.
quantity("50k").sub(20000)Відняти ціле число від кількості.
quantity("50k").add(20).sub(quantity("100k")).sub(-50000)Ланцюгове додавання та віднімання цілих чисел та кількостей.
quantity("200M").compareTo(quantity("0.2G"))Порівняти дві кількості.
quantity("150Mi").isGreaterThan(quantity("100Mi"))Перевірити, чи кількість більша за отримувача.
quantity("50M").isLessThan(quantity("100M"))Перевірити, чи кількість менша за отримувача.

Бібліотека semver Kubernetes

У Kubernetes v1.34 додано підтримку розбору та порівняння рядків, що відповідають специфікації Semantic Versioning 2.0.0. Докладніше про прийняті патерни дивіться semver.org.

  • isSemver(string) перевіряє, чи є рядок валідною семантичною версією.
  • semver(string) конвертує рядок у обʼєкт Semver або повертає помилку.

Додатковий булевий аргумент normalize можна передати у isSemver та semver. Якщо true, нормалізація прибирає префікс "v", додає 0 для minor та patch, якщо вказано лише major або major.minor, і прибирає ведучі нулі.

Після розбору через функцію semver отриманий обʼєкт Semver має такі функції-члени:

Доступні функції-члени обʼєкту Semver

Функція-членТип (CEL)Опис
major()intПовертає номер основної версії (major).
minor()intПовертає номер другорядної версії (minor).
patch()intПовертає номер патч-версії (patch).
isLessThan(<Semver>)boolПовертає true, якщо поточна версія менша за аргумент.
isGreaterThan(<Semver>)boolПовертає true, якщо поточна версія більша за аргумент.
compareTo(<Semver>)intПорівнює поточну версію з аргументом: повертає 0, якщо рівні, 1 — якщо поточна більша, -1 — якщо менша.

Приклади:

Приклади CEL-виразів з використанням функцій бібліотеки semver

Вираз CELПризначення
isSemver('1.0.0')Повертає true для валідного рядка Semver.
isSemver('v1.0', true)Повертає true для нормалізованого рядка Semver.
semver('1.2.3').major()Повертає основну версію Semver.
semver('1.2.3').compareTo(semver('2.0.0')) < 0Порівнює дві версії Semver.

Докладніше: Бібліотека semver Kubernetes godoc.

Перевірка типів

CEL — це поступово типізована мова.

Деякі поля API Kubernetes містять повністю перевірені типи CEL-виразів. Наприклад, Правила валідації власних ресурсів повністю перевірені за типом.

Деякі поля API Kubernetes містять частково перевірені типи CEL-виразів. Частково перевірений вираз — це вираз, в якому деякі змінні статично типізовані, а інші — динамічно типізовані. Наприклад, в CEL-виразах ValidatingAdmissionPolicies, змінна request має тип, але змінна object динамічно типізована. У звʼязку з цим вираз, що містить request.namex, не пройде перевірку типів, оскільки поле namex не визначене. Однак object.namex пройде перевірку типів навіть тоді, коли поле namex не визначене для типів ресурсів, на які посилається object, оскільки object динамічно типізований.

Макрос has() в CEL можна використовувати у виразах CEL для перевірки доступності поля динамічно типізованої змінної перед спробою доступу до значення поля. Наприклад:

has(object.namex) ? object.namex == 'special' : request.name == 'special'

Інтеграція системи типів

Таблиця, що показує взаємозвʼязок між типами OpenAPIv3 та CEL
Тип OpenAPIv3Тип CEL
'object' з Propertiesobject / "тип повідомлення" (type(<object>) обчислюється як selfType<uniqueNumber>.path.to.object.from.self
'object' з AdditionalPropertiesmap
'object' з x-kubernetes-embedded-typeobject / "тип повідомлення", 'apiVersion', 'kind', 'metadata.name' і 'metadata.generateName' включені в схему
'object' з x-kubernetes-preserve-unknown-fieldsobject / "тип повідомлення", невідомі поля НЕ доступні у виразі CEL
x-kubernetes-int-or-stringобʼєднання int або string, self.intOrString < 100 || self.intOrString == '50%' обчислюється як true для 50 і "50%"
'array'list
'array' з x-kubernetes-list-type=maplist з базованими на map рівноправністю та унікальними ключами
'array' з x-kubernetes-list-type=setlist з базованими на set рівноправністю та унікальними елементами
'boolean'boolean
'number' (усі формати)double
'integer' (усі формати)int (64)
немає еквівалентаuint (64)
'null'null_type
'string'string
'string' з format=byte (base64 encoded)bytes
'string' з format=datetimestamp (google.protobuf.Timestamp)
'string' з format=datetimetimestamp (google.protobuf.Timestamp)
'string' з format=durationduration (google.protobuf.Duration)

Також дивіться: Типи CEL, Типи OpenAPI, Структурні схеми Kubernetes.

Порівняння рівності для масивів з x-kubernetes-list-type типу set або map ігнорує порядок елементів. Наприклад, [1, 2] == [2, 1] якщо масиви представляють значення Kubernetes set.

Конкатенація для масивів з x-kubernetes-list-type використовує семантику типу списку:

set
X + Y виконує обʼєднання, де позиції елементів у X зберігаються, а не перетинаючі елементи у Y додаються, зберігаючи їх частковий порядок.
map
X + Y виконує обʼєднання, де позиції ключів у X зберігаються, але значення перезаписуються значеннями у Y, коли ключові множини X і Y перетинаються. Елементи у Y з неперетинаючими ключами додаються, зберігаючи їх частковий порядок.

Екранування

Тільки імена властивостей ресурсів Kubernetes форми [a-zA-Z_.-/][a-zA-Z0-9_.-/]* доступні з CEL. Доступні імена властивостей екрануються згідно з наступними правилами при доступі у виразі:

Таблиця правил екранування ідентифікаторів CEL
екрануюча послідовністьеквівалент імені властивості
__underscores____
__dot__.
__dash__-
__slash__/
__{keyword}__ЗАРЕЗЕРВОВАНЕ ключове слово CEL

Коли ви екрануєте будь-яке з ЗАРЕЗЕРВОВАНИХ ключових слів CEL, збіг повинен точно відповідати імені властивості та використовувати екранування з підкресленням (наприклад, int у слові sprint не буде екрановано, і це не буде необхідно).

Приклади екранування:

Приклади екранованих ідентифікаторів CEL
імʼя властивостіправило з екранованим імʼям властивості
namespaceself.__namespace__ > 0
x-propself.x__dash__prop > 0
redact__dself.redact__underscores__d > 0
stringself.startsWith('kube')

Обмеження ресурсів

CEL не є повноцінною мовою Тюрінга і пропонує різноманітні засоби безпеки для обмеження часу виконання. Функції обмеження ресурсів CEL забезпечують зворотний звʼязок розробникам щодо складності виразів та допомагають захистити сервер API від надмірного споживання ресурсів під час оцінювання. Ці функції використовуються для запобігання надмірного споживання ресурсів сервера API під час виконання CEL.

Ключовим елементом функцій обмеження ресурсів є одиниця вартості, яку CEL визначає як спосіб відстеження використання ЦП. Одиниці вартості незалежні від системного навантаження та апаратного забезпечення. Одиниці вартості також є детермінованими; для будь-якого заданого виразу CEL та вхідних даних, оцінка виразу інтерпретатором CEL завжди призведе до однакової вартості.

Багато з основних операцій CEL мають фіксовані витрати. Найпростіші операції, такі як порівняння (наприклад, <), мають вартість 1. Деякі мають вищу фіксовану вартість, наприклад, оголошення літералів списку мають фіксовану базову вартість 40 одиниць вартості.

Виклики функцій, реалізованих у рідному коді, оцінюються на основі часової складності операції. Наприклад, операції, що використовують регулярні вирази, такі як match та find, оцінюються з використанням приблизної вартості length(regexString)*length(inputString). Приблизна вартість відображає найгірший випадок часової складності реалізації RE2 в Go.

Бюджет вартості під час виконання

Усі вирази CEL, які оцінюються Kubernetes, обмежені бюджетом вартості під час виконання. Бюджет вартості під час виконання — це оцінка фактичного використання ЦП, що обчислюється шляхом інкрементування лічильника одиниць вартості під час інтерпретації виразу CEL. Якщо інтерпретатор CEL виконає занадто багато інструкцій, бюджет вартості під час виконання буде перевищено, виконання виразу буде зупинено і результатом стане помилка.

Деякі ресурси Kubernetes визначають додатковий бюджет вартості під час виконання, який обмежує виконання декількох виразів. Якщо загальна вартість виразів перевищує бюджет, виконання виразів буде зупинено і результатом стане помилка. Наприклад, валідація власного ресурсу користувача має бюджет вартості під час виконання за одну валідацію для всіх Правил Валідації, які оцінюються для валідації власного ресурсу.

Оцінювані обмеження вартості

Для деяких ресурсів Kubernetes, сервер API також може перевірити, чи не буде найгірший випадок оціненого часу виконання виразів CEL надто дорогим для виконання. Якщо так, сервер API запобігає записуванню виразу CEL у ресурсі API, відхиляючи операції створення або оновлення, що містять вираз CEL у ресурсі API. Ця функція пропонує сильнішу гарантію, що вирази CEL, записані у ресурс API, будуть оцінені під час виконання без перевищення бюджету вартості під час виконання.

Змінено July 19, 2025 at 6:51 PM PST: sync upstream (d9621c798a)