Загальна мова виразів (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:
| Правило | Призначення |
|---|---|
| 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' пізніша за дату 'create' плюс тривалість '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' | Перевірити, що імʼя обʼєкта відповідає конкретному значенню (що робить його єдиним екземпляром — singleton) |
| self.set1.all(e, !(e in self.set2)) | Перевірити, що два listSet не перетинаються |
| self.names.size() == self.details.size() && self.names.all(n, n in self.details) | Перевірити, що ключами map 'details' є елементи listSet '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 налаштовується з наступними опціями, бібліотеками та можливостями мови, введеними у зазначених версіях Kubernetes:
| Опція, бібліотека або можливість мови CEL | Включено | Доступність |
|---|---|---|
| Стандартні макроси | has, all, exists, exists_one, map, filter | Усі версії Kubernetes |
| Стандартні функції | Дивіться офіційний список стандартних визначень | Усі версії Kubernetes |
| Однорідні агрегатні літерали | - | Усі версії Kubernetes |
| Типовий часовий пояс UTC | - | Усі версії Kubernetes |
| Рання перевірка декларацій | - | Усі версії Kubernetes |
| Розширена бібліотека рядків, версія 1 | charAt, indexOf, lastIndexOf, lowerAscii,
upperAscii, replace, split, join, substring,
trim | Версії Kubernetes від 1.25 до 1.30 |
| Розширена бібліотека рядків, версія 2 | charAt, 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, які доступні у всіх місцях використання CEL в Kubernetes.
Бібліотека списків включає indexOf та lastIndexOf, які працюють аналогічно функціям рядків з такими самими назвами. Ці функції повертають перший або останній індекс елемента у списку.
Бібліотека списків також включає min, max та sum. Сума підтримується для всіх типів чисел, а також для типу тривалості. Мінімум та максимум підтримуються для всіх типів, що можна порівняти.
Також надається функція isSorted для зручності та підтримується для всіх типів, які можна порівняти.
Приклади:
| Вираз 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.
Крім функції matches, наданої стандартною бібліотекою CEL, бібліотека регулярних виразів надає функції find та findAll, що дозволяють виконувати значно ширший спектр операцій з регулярними виразами.
Приклади:
| Вираз CEL | Призначення |
| "abc 123".find('[0-9]+') | Знайти перше число у рядку |
| "1, 2, 3, 4".findAll('[0-9]+').map(x, int(x)).sum() < 100 | Перевірити, що сума чисел у рядку менша за 100 |
Для отримання додаткової інформації дивіться бібліотеку регулярних виразів Kubernetes godoc.
Для спрощення та безпечної обробки URL надані наступні функції:
isURL(string) перевіряє, чи є рядок дійсним URL відповідно до пакета
net/url у Go. Рядок повинен бути абсолютним URL.url(string) URL конвертує рядок в URL або викликає помилку, якщо рядок не є дійсним URL.Після розбору за допомогою функції url, отриманий обʼєкт URL має наступні методи доступу: getScheme, getHost, getHostname, getPort, getEscapedPath та getQuery.
Приклади:
| Вираз 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-адрес простішою і безпечнішою, були додані наступні функції:
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 має наступну бібліотеку функцій-членів:
| Функція-член | Значення, що повертається (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-адреса, що не є локальною індивідуальною адресою каналу, loopback-адресою або груповою адресою. |
Приклади:
| Вираз 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 надає розширення бібліотеки функцій CEL для розбору нотації CIDR.
cidrКонвертує рядок у нотації CIDR в представлення мережевої адреси або викликає помилку, якщо рядок не є дійсною нотацією CIDR. CIDR повинен бути підмережею IPv4 або IPv6 з маскою. Ведучі нулі в октетах IPv4-адреси не дозволяються. IPv4-адреси, що відображаються в IPv6 (наприклад, ::ffff:1.2.3.4/24), не дозволяються.
cidr(<string>) <CIDR>
Приклади:
| Вираз CEL | Призначення |
|---|---|
| cidr('192.168.0.0/16') | Повертає IPv4-адресу з маскою CIDR. |
| cidr('::1/128') | Повертає IPv6-адресу з маскою CIDR. |
| cidr('192.168.0.0/33') | Помилка, оскільки довжина префікса перевищує розмір IPv4-адреси. |
| cidr('::1/129') | Помилка, оскільки довжина префікса перевищує розмір IPv6-адреси. |
| cidr('192.168.0.1/16') | Помилка, оскільки після префікса є біти, що не дорівнюють 0. |
isCIDRПовертає true, якщо рядок є дійсним представленням підмережі з маскою в нотації CIDR. CIDR повинен бути підмережею IPv4 або IPv6 з маскою. Ведучі нулі в октетах IPv4-адреси не дозволяються. IPv4-адреси, що відображаються в IPv6 (наприклад, ::ffff:1.2.3.4/24), не дозволяються.
isCIDR(<string>) <bool>
Приклади:
| Вираз CEL | Призначення |
|---|---|
| isCIDR('192.168.0.0/16') | Повертає true для дійсного IPv4 CIDR. |
| isCIDR('::1/128') | Повертає true для дійсного IPv6 CIDR. |
| isCIDR('192.168.0.0/33') | Повертає false, оскільки довжина префікса перевищує розмір IPv4-адреси. |
| isCIDR('::1/129') | Повертає false, оскільки довжина префікса перевищує розмір IPv6-адреси. |
containsIP / containsCIDR / ip / masked / prefixLengthcontainsIP: Повертає 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('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.
Для виразів CEL в API, де доступна змінна типу Authorizer, авторизатор може використовуватися для виконання перевірок авторизації для принципала (автентифікованого користувача) запиту.
Перевірки ресурсів API виконуються наступним чином:
Authorizer.group(string).resource(string) ResourceCheckResourceCheck.subresource(string) ResourceCheckResourceCheck.namespace(string) ResourceCheckResourceCheck.name(string) ResourceCheckResourceCheck.check(verb string) Decision, щоб виконати перевірку авторизації.allowed() bool або reason() string, щоб переглянути результат перевірки авторизації.Авторизація для нересурсних запитів виконується так:
Authorizer.path(string) PathCheckPathCheck.check(httpVerb string) Decision, щоб виконати перевірку авторизації.allowed() bool або reason() string, щоб переглянути результат перевірки авторизації.Для виконання перевірки авторизації для службового облікового запису:
Authorizer.serviceAccount(namespace string, name string) Authorizer| Вираз 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() | Перевіряє, чи має службовий обліковий запис дозвіл видаляти Deploymentʼи. |
This is a stable feature in Kubernetes, and has been since version 1.34. It was first available in the v1.31 release.
Для виразів CEL в API, де доступна змінна типу Authorizer, селектори полів і міток можуть бути включені в перевірки авторизації.
| Вираз 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.
Бібліотека format надає функції для перевірки поширених форматів рядків у Kubernetes. Це може бути корисно у messageExpression правил валідації для надання більш конкретних повідомлень про помилки.
Бібліотека надає функції format() для кожного іменованого формату, а також загальну функцію format.named().
format.named(string) → ?Format: Повертає обʼєкт Format для заданого імені формату, якщо він існує. Інакше повертає optional.none.format.<formatName>() -> Format: Також доступні зручні функції для всіх іменованих форматів. Наприклад, format.dns1123Label() повертає обʼєкт Format для міток DNS-1123.<Format>.validate(string) -> list<string>?: Перевіряє рядок на відповідність формату. Повертає optional.none, якщо рядок валідний, інакше — optional, що містить список рядків з помилками валідації.Доступні формати:
Підтримуються наступні назви форматів:
| Назва формату | Опис |
|---|---|
| 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 | Призначення |
|---|---|
| !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 1.28 додана підтримка обробки рядків кількості (наприклад, 1.5G, 512k, 20Mi).
isQuantity(string) перевіряє, чи є рядок дійсною кількістю (Quantity) відповідно до
resource.Quantity у Kubernetes.quantity(string) Quantity конвертує рядок у кількість або викликає помилку, якщо рядок не є дійсною кількістю.Після розбору за допомогою функції quantity отриманий обʼєкт Quantity має таку бібліотеку функцій-членів:
| Функція-член | Значення, що повертається (CEL) | Опис | |
|---|---|---|---|
| isInteger() | bool | Повертає true тоді й лише тоді, коли asInteger можна безпечно викликати без помилки | |
| asInteger() | int | Повертає представлення поточного значення як int64, якщо це можливо, або викликає помилку, якщо конвертація призводить до переповнення або втрати точності. | |
| asApproximateFloat() | float | Повертає представлення кількості як float64, яке може втратити точність. Якщо значення кількості виходить за межі діапазону float64, буде повернуто +Inf/-Inf. | |
| 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 | Призначення |
|---|---|
| quantity("500000G").isInteger() | Перевірка, чи конвертація в ціле число викликає помилку |
| quantity("50k").asInteger() | Точна конвертація в ціле число |
| quantity("9999999999999999999999999999999999999G").asApproximateFloat() | Конвертація у float із втратою точності |
| 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")) | Перевірити, чи кількість менша за отримувача |
У 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 має такі функції-члени:
| Функція-член | Значення, що повертається (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 | Призначення |
|---|---|
| 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 з повною перевіркою типів. Наприклад, правила валідації CustomResourceDefinitions проходять повну перевірку типів.
Деякі поля 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'
Використовуйте has() для перевірки наявності поля. Не використовуйте has() для перевірки наявності ключа в map. Наприклад, не пишіть has(object.metadata.labels['example.com/environment']). Для перевірки ключів у map використовуйте оператор in. Наприклад:
has(object.metadata.labels) && 'example.com/environment' in object.metadata.labels
Цей вираз перевіряє, що metadata.labels присутній перед перевіркою наявності ключа example.com/environment у map.
| Тип OpenAPIv3 | Тип CEL |
|---|---|
| 'object' з Properties | object / "тип повідомлення" (type(<object>) обчислюється як selfType<uniqueNumber>.path.to.object.from.self) |
| 'object' з additionalProperties | map |
| 'object' з x-kubernetes-embedded-type | object / "тип повідомлення", 'apiVersion', 'kind', 'metadata.name' і 'metadata.generateName' неявно включені в схему |
| 'object' з x-kubernetes-preserve-unknown-fields | object / "тип повідомлення", невідомі поля НЕ доступні у виразі CEL |
| x-kubernetes-int-or-string | Обʼєднання int або string, self.intOrString < 100 | self.intOrString == '50%' обчислюється як true і для 50, і для "50%" |
| 'array' | list |
| 'array' з x-kubernetes-list-type=map | list з рівністю на основі map і гарантіями унікальності ключів |
| 'array' з x-kubernetes-list-type=set | list з рівністю на основі set і гарантіями унікальності елементів |
| 'boolean' | boolean |
| 'number' (усі формати) | double |
| 'integer' (усі формати) | int (64) |
| немає еквівалента | uint (64) |
| 'null' | null_type |
| 'string' | string |
| 'string' з format=byte (закодований у base64) | bytes |
| 'string' з format=date | timestamp (google.protobuf.Timestamp) |
| 'string' з format=datetime | timestamp (google.protobuf.Timestamp) |
| 'string' з format=duration | duration (google.protobuf.Duration) |
Також дивіться: Типи CEL, Типи OpenAPI, Структурні схеми Kubernetes.
Порівняння рівності для масивів з x-kubernetes-list-type типу set або map ігнорує порядок елементів. Наприклад, [1, 2] == [2, 1], якщо масиви представляють значення Kubernetes set.
Конкатенація для масивів з x-kubernetes-list-type використовує семантику типу списку:
setX + Y виконує обʼєднання, за якого позиції всіх елементів у X у масиві зберігаються, а елементи в Y, що не перетинаються, додаються в кінець зі збереженням їх часткового порядку.mapX + Y виконує злиття, за якого позиції всіх ключів у X у масиві зберігаються, але значення перезаписуються значеннями з Y, коли множини ключів X і Y перетинаються. Елементи Y з ключами, що не перетинаються, додаються в кінець зі збереженням їх часткового порядку.Тільки імена властивостей ресурсів Kubernetes форми [a-zA-Z_.-/][a-zA-Z0-9_.-/]* доступні з CEL. Доступні імена властивостей екрануються згідно з наступними правилами при доступі у виразі:
| екрануюча послідовність | еквівалент імені властивості |
|---|---|
| __underscores__ | __ |
| __dot__ | . |
| __dash__ | - |
| __slash__ | / |
| __{keyword}__ | ЗАРЕЗЕРВОВАНЕ ключове слово CEL |
Коли ви екрануєте будь-яке з ЗАРЕЗЕРВОВАНИХ ключових слів CEL, збіг повинен точно відповідати імені властивості та використовувати екранування з підкресленням (наприклад, int у слові sprint не буде екрановано, і це не буде необхідно).
Приклади екранування:
| імʼя властивості | правило з екранованим імʼям властивості |
|---|---|
| namespace | self.__namespace__ > 0 |
| x-prop | self.x__dash__prop > 0 |
| redact_d | self.redact__underscores__d > 0 |
| string | self.startsWith('kube') |
CEL не є Тюрінг-повною мовою і пропонує різноманітні засоби безпеки для промислової експлуатації, що обмежують час виконання. Функції обмеження ресурсів CEL забезпечують зворотний звʼязок розробникам щодо складності виразів та допомагають захистити API-сервер від надмірного споживання ресурсів під час обчислення. Функції обмеження ресурсів CEL використовуються для запобігання надмірному споживанню ресурсів API-сервера під час обчислення CEL.
Ключовим елементом функцій обмеження ресурсів є одиниця вартості, яку CEL визначає як спосіб відстеження використання CPU. Одиниці вартості незалежні від системного навантаження та апаратного забезпечення. Одиниці вартості також є детермінованими; для будь-якого заданого виразу CEL та вхідних даних обчислення виразу інтерпретатором CEL завжди призведе до однакової вартості.
Багато з основних операцій CEL мають фіксовані витрати. Найпростіші операції, такі як порівняння (наприклад, <), мають вартість 1. Деякі мають вищу фіксовану вартість, наприклад, оголошення літералів списку мають фіксовану базову вартість 40 одиниць вартості.
Виклики функцій, реалізованих у рідному коді, оцінюються на основі часової складності операції. Наприклад, операції, що використовують регулярні вирази, такі як match та find, оцінюються з використанням приблизної вартості length(regexString)*length(inputString). Приблизна вартість відображає найгірший випадок часової складності реалізації RE2 в Go.
Усі вирази CEL, які обчислює Kubernetes, обмежені бюджетом вартості під час виконання. Бюджет вартості під час виконання — це оцінка фактичного використання CPU, що обчислюється шляхом інкрементування лічильника одиниць вартості під час інтерпретації виразу CEL. Якщо інтерпретатор CEL виконає занадто багато інструкцій, бюджет вартості під час виконання буде перевищено, виконання виразу буде зупинено і результатом стане помилка.
Деякі ресурси Kubernetes визначають додатковий бюджет вартості під час виконання, який обмежує виконання декількох виразів. Якщо загальна вартість виразів перевищує бюджет, виконання виразів буде зупинено і результатом стане помилка. Наприклад, валідація власного ресурсу користувача має бюджет вартості під час виконання на одну валідацію для всіх правил валідації, які обчислюються для валідації власного ресурсу.
Для деяких ресурсів Kubernetes API-сервер також може перевірити, чи не буде найгірший випадок оціненого часу виконання виразів CEL надто дорогим для виконання. Якщо так, API-сервер запобігає записуванню виразу CEL у ресурси API, відхиляючи операції створення або оновлення, що містять вираз CEL для ресурсів API. Ця функція пропонує сильнішу гарантію, що вирази CEL, записані у ресурс API, будуть обчислені під час виконання без перевищення бюджету вартості під час виконання.