Декларативна перевірка API
Kubernetes v1.33 [beta]Kubernetes 1.35 включає необовʼязкову декларативну перевірку для API. Якщо її увімкнено, сервер API Kubernetes може використовувати цей механізм, а не застарілий підхід, який покладається на рукописний код Go (файли validation.go), щоб гарантувати, що запити до API є дійсними. Розробники Kubernetes та люди, які розширюють Kubernetes API, можуть визначати правила валідації безпосередньо поряд з визначеннями типів API (файли types.go). Автори коду визначають спеціальні теґи коментарів (наприклад, +k8s:minimum=0). Генератор коду (validation-gen) потім використовує ці теґи для створення оптимізованого коду Go для перевірки API.
Хоча ця функція в першу чергу впливає на учасників Kubernetes і потенційних розробників серверів API розширень, адміністратори кластерів повинні розуміти її поведінку, особливо на етапах її розгортання.
Декларативна перевірка розгортається поступово. У Kubernetes 1.35, API, які використовують декларативну перевірку, включають:
Примітка:
Для бета-версії цієї функції Kubernetes навмисно використовує замінений API як тестовий майданчик для змін. У майбутніх випусках Kubernetes цю можливість може бути поширено на інші API.DeclarativeValidation: (Beta, стандартно:true) Якщо увімкнено, сервер API виконує як нову декларативну перевірку, так і стару ручну перевірку для перенесених типів/полів. Результати порівнюються внутрішньо.DeclarativeValidationTakeover: (Beta, стандартно:false) Ця можливість визначає, який результат перевірки є авторитетним (тобто, повертається користувачеві і використовується для прийняття рішень про допуск).
Типова поведінка (Kubernetes 1.35):
- Зі значеннями
DeclarativeValidation=trueтаDeclarativeValidationTakeover=false( стандартні значення для функціональних можливостей) працюють обидві системи валідації. - Використовуються результати рукописної валідації. Декларативна валідація запускається у режимі розбіжностей для порівняння.
- Невідповідності між двома системами валідації реєструються сервером API і збільшують метрику
declarative_validation_mismatch_total. Це допомагає розробникам виявляти та виправляти розбіжності на етапі бета-версії. - Оновлення кластерів має бути безпечним щодо цієї функції, оскільки логіка авторитетної перевірки стандартно не змінюється.
Адміністратори можуть явно ввімкнути DeclarativeValidationTakeover=true, щоб зробити декларативну перевірку авторитетною для перенесених полів, зазвичай після перевірки стабільності в їхньому середовищі (наприклад, шляхом моніторингу метрики невідповідностей).
Вимкнення декларативної перевірки
Як адміністратор кластера, ви можете вимкнути декларативну перевірку, поки вона ще є бета-версією, за певних обставин:
- Неочікувана поведінка перевірки: Якщо увімкнення
DeclarativeValidationTakeoverпризводить до неочікуваних помилок перевірки або дозволяє обʼєкти, які раніше були недійсними. - Погіршення продуктивності: Якщо моніторинг вказує на значне збільшення затримок (наприклад, у
apiserver_request_duration_seconds), повʼязане з увімкненням цієї функції. - Високий рівень невідповідностей: Якщо метрика
declarative_validation_mismatch_totalпоказує часті невідповідності, що свідчить про потенційні помилки в декларативних правилах, які впливають на робочі навантаження кластера, навіть якщоDeclarativeValidationTakeoverмає значення false.
Щоб повернутися до використання лише рукописної перевірки (як це було до Kubernetes v1.33), вимкніть DeclarativeValidation feature gate, наприклад, за допомогою аргументів командного рядка: (--feature-gates=DeclarativeValidation=false). Це також неявно вимкне ефект DeclarativeValidationTakeover.
Міркування щодо пониження версії та відкату
Вимкнення цієї функції діє як механізм безпеки. Однак, памʼятайте про потенційний крайній випадок (який вважається малоймовірним завдяки ретельному тестуванню): Якщо помилка у декларативній перевірці (коли DeclarativeValidationTakeover=true) неправильно дозволила зберегти недійсний обʼєкт, вимкнення функціональних можливостей може призвести до блокування подальших оновлень цього конкретного обʼєкта за допомогою тепер авторизованої (і правильної) ручної перевірки. Розвʼязання цієї проблеми може вимагати ручного виправлення збереженого обʼєкта, можливо, за допомогою прямої модифікації etcd у рідкісних випадках.
Докладні відомості про керування функціональними можливостями наведено у статті Функціональні можливості.
Довідник теґів декларативної валідації
Цей документ містить вичерпну довідкову інформацію про всі доступні теґи декларативної валідації.
Каталог теґів
| Теґ | Опис |
|---|---|
+k8s:eachKey | Оголошує валідацію для кожного ключа в map. |
+k8s:eachVal | Оголошує валідацію для кожного значення в map або списку. |
+k8s:enum | Вказує, що тип рядка є enum. |
+k8s:forbidden | Вказує, що поле не може бути вказане. |
+k8s:format | Вказує, що рядкове поле має певний формат. |
+k8s:ifDisabled | Оголошує валідацію, яка застосовується, коли опцію вимкнено. |
+k8s:ifEnabled | Оголошує валідацію, яка застосовується, коли опцію увімкнено. |
+k8s:isSubresource | Визначає, що валідації в пакеті застосовуються лише до конкретного субресурсу. |
+k8s:item | Оголошує валідацію для елемента зрізу, оголошеного як +k8s:listType=map. |
+k8s:listMapKey | Оголошує іменоване підполе типу значення списку як частину ключа list-map. |
+k8s:listType | Оголошує семантичний тип поля списку. |
+k8s:maxItems | Вказує, що поле списку має обмеження на розмір. |
+k8s:maxLength | Вказує, що рядкове поле має обмеження на довжину. |
+k8s:minimum | Вказує, що числове поле має мінімальне значення. |
+k8s:neq | Перевіряє, що значення поля не дорівнює конкретному забороненому значенню. |
+k8s:opaqueType | Вказує, що будь-які валідації, оголошені для вказаного типу, будуть проігноровані. |
+k8s:optional | Вказує, що поле є необовʼязковим для клієнтів. |
+k8s:required | Вказує, що поле має бути вказане клієнтами. |
+k8s:subfield | Оголошує валідацію для підполя структури. |
+k8s:supportsSubresource | Оголошує підтримуваний субресурс для типів у пакеті. |
+k8s:unionDiscriminator | Вказує, що це поле є дискримінатором для обʼєднання. |
+k8s:unionMember | Вказує, що це поле є членом групи обʼєднання. |
+k8s:zeroOrOneOfMember | Вказує, що це поле є членом групи нуль-або-один-з. |
Довідник по теґам
+k8s:eachKey
Опис:
Оголошує валідацію для кожного ключа в map.
Корисне навантаження:
<validation-tag>: Теґ для оцінки кожного ключа.
Приклад використання:
type MyStruct struct {
// +k8s:eachKey=+k8s:minimum=1
MyMap map[int]string `json:"myMap"`
}
У цьому прикладі eachKey використовується для вказівки, що теґ +k8s:minimum повинен застосовуватися до кожного ключа int в MyMap. Це означає, що всі ключі в map повинні бути >= 1.
+k8s:eachVal
Опис:
Оголошує валідацію для кожного значення в map або списку.
Корисне навантаження:
<validation-tag>: Теґ для оцінки для кожного значення.
Приклад використання:
type MyStruct struct {
// +k8s:eachVal=+k8s:minimum=1
MyMap map[string]int `json:"myMap"`
}
У цьому прикладі eachVal використовується для вказівки, що теґ +k8s:minimum повинен застосовуватися до кожного елемента в MyList. Це означає, що всі поля в MyStruct повинні бути >= 1.
+k8s:enum
Опис:
Вказує, що тип рядка є enum (перерахуванням). Усі константи цього типу вважаються значеннями в перерахуванні.
Приклад використання:
Спочатку визначте новий тип рядка та кілька констант цього типу:
// +k8s:enum
type MyEnum string
const (
MyEnumA MyEnum = "A"
MyEnumB MyEnum = "B"
)
Потім використовуйте цей тип в іншій структурі:
type MyStruct struct {
MyField MyEnum `json:"myField"`
}
Логіка валідації забезпечить те, що MyField є одним з визначених значень enum (перерахування) ("A" або "B").
+k8s:forbidden
Опис:
Вказує, що поле не може бути вказане.
Приклад використання:
type MyStruct struct {
// +k8s:forbidden
MyField string `json:"myField"`
}
У цьому прикладі MyField не може бути надане (воно заборонене) під час створення або оновлення MyStruct.
+k8s:format
Опис:
Вказує, що рядкове поле має певний формат.
Корисне навантаження:
k8s-ip: Це поле містить значення IPv4 або IPv6 адреси. Октети IPv4 можуть мати нулі на початку.k8s-long-name: Це поле містить "довгу назву" Kubernetes, також відому як значення "DNS піддомену".k8s-short-name: Це поле містить "коротку назву" Kubernetes, також відому як значення "DNS мітки".
Приклад використання:
type MyStruct struct {
// +k8s:format=k8s-ip
IPAddress string `json:"ipAddress"`
// +k8s:format=k8s-long-name
Subdomain string `json:"subdomain"`
// +k8s:format=k8s-short-name
Label string `json:"label"`
}
У цьому прикладі:
IPAddressповинен бути дійсною IP-адресою.Subdomainповинен бути дійсним піддоменом DNS.Labelповинен бути дійсною міткою DNS.
+k8s:ifDisabled
Опис:
Оголошує валідацію, яка застосовується, коли опцію вимкнено.
Аргументи:
<option>(рядок, обовʼязково): Імʼя опції.
Корисне навантаження:
<validation-tag>: Цей теґ валідації буде оцінюватися лише тоді, коли опцію вимкнено.
Приклад використання:
type MyStruct struct {
// +k8s:ifDisabled("my-feature")=+k8s:required
MyField string `json:"myField"`
}
У цьому прикладі MyField є обовʼязковим лише тоді, коли опція "my-feature" вимкнена.
+k8s:ifEnabled
Опис:
Оголошує валідацію, яка застосовується, коли опцію увімкнено.
Аргументи:
<option>(рядок, обовʼязково): Імʼя опції.
Корисне навантаження:
<validation-tag>: Цей теґ валідації буде оцінюватися лише тоді, коли опцію увімкнено.
Приклад використання:
type MyStruct struct {
// +k8s:ifEnabled("my-feature")=+k8s:required
MyField string `json:"myField"`
}
У цьому прикладі MyField є обовʼязковим лише тоді, коли опція "my-feature" увімкнена.
+k8s:isSubresource
Опис:
Теґ +k8s:isSubresource є коментарем на рівні пакета, який обмежує правила валідації в межах цього пакета до конкретного субресурсу. Він по суті говорить генератору коду: "Логіка валідації, визначена тут, є специфічною реалізацією для цього субресурсу і не повинна застосовуватися до кореневого обʼєкта або будь-якого іншого субресурсу."
КРИТИЧНА ЗАЛЕЖНІСТЬ:
Цей теґ є залежним від відповідного теґа +k8s:supportsSubresource, який повинен бути присутній у пакеті, де визначено основний тип API.
+k8s:supportsSubresourceвідкриває двері, кажучи диспетчеру, що субресурс є дійсним.+k8s:isSubresourceнадає спеціалізовану логіку валідації, яка виконується, коли запит проходить через ці двері.
Якщо ви використовуєте +k8s:isSubresource без відповідного оголошення +k8s:supportsSubresource для основного типу, спеціалізований код валідації буде згенеровано, але він буде недоступний. Основний диспетчер не розпізнає шлях субресурсу і відхилить запит, перш ніж він зможе бути надісланий до вашої специфічної логіки валідації.
Ця залежність дозволяє потужну організацію, таку як розміщення ваших основних типів API в одному пакеті та визначення їх специфічних для субресурсу валідацій у окремих, спеціалізованих пакетах.
Область дії: Пакет
Корисне навантаження:
<subresource-path>: Шлях субресурсу, до якого повинні застосовуватися валідації в цьому пакеті (наприклад,"/status","/scale").
Приклад використання:
Цей приклад з двох частин демонструє передбачуваний випадок використання розділення обовʼязків.
1. Оголосити підтримку в основному пакеті API:
Спочатку оголосіть, що тип Deployment підтримує валідацію /scale у своєму основному пакеті.
Файл: staging/src/k8s.io/api/apps/v1/doc.go
// Це дозволяє диспетчеру валідації обробляти запити для "/scale".
// +k8s:supportsSubresource="/scale"
package v1
// ... включає визначення типу Deployment
2. Обмежити логіку валідації в окремому пакеті:
Далі створіть окремий пакет для правил валідації, які специфічні тільки для субресурсу /scale.
Файл: staging/src/k8s.io/api/apps/v1/validations/scale/doc.go
// Це забезпечує те, що правила в цьому пакеті виконуються ТІЛЬКИ для субресурсу "/scale".
// +k8s:isSubresource="/scale"
package scale
import "k8s.io/api/apps/v1"
// Код валідації в цьому пакеті буде посилатися на типи з пакета v1 (наприклад, v1.Scale).
// Згенерована функція валідації буде викликатися лише для запитів до субресурсу "/scale"
// обʼєкта типу, визначеного в пакеті, який підтримує це.
+k8s:item
Опис:
Оголошує валідацію для елемента зрізу, оголошеного як +k8s:listType=map. Елемент, що має збіг, оголошується шляхом надання аргументів парою поле-значення, де поле є listMapKey. Усі поля ключів listMapKey повинні бути вказані.
Використання:
+k8s:item(<listMapKey-JSON-field-name>: <value>,...)=<validation-tag>
+k8s:item(stringKey: "value", intKey: 42, boolKey: true)=<validation-tag>
Аргументи повинні бути іменовані за допомогою JSON-імен полів ключів списку-мапи. Значення можуть бути рядками, цілими числами або булевими значеннями.
Корисне навантаження:
<validation-tag>: Теґ для оцінки для елемента списку, що має збіг.
Приклад використання:
type MyStruct struct {
// +k8s:listType=map
// +k8s:listMapKey=type
// +k8s:item(type: "Approved")=+k8s:zeroOrOneOfMember
// +k8s:item(type: "Denied")=+k8s:zeroOrOneOfMember
MyConditions []MyCondition `json:"conditions"`
}
type MyCondition struct {
Type string `json:"type"`
Status string `json:"status"`
}
У цьому прикладі:
- Умова з
type"Approved" є частиною групи нуль-або-один-з. - Умова з
type"Denied" є частиною групи нуль-або-один-з.
+k8s:listMapKey
Опис:
Оголошує іменоване підполе типу значення списку як частину ключа list-map. Цей теґ є обовʼязковим, коли використовується +k8s:listType=map. Кілька теґів +k8s:listMapKey можуть використовуватися на list-map, щоб вказати, що він є ключем з кількох полів.
Корисне навантаження:
<field-json-name>: Імʼя JSON поля, яке буде використовуватися як ключ.
Приклад використання:
// +k8s:listType=map
// +k8s:listMapKey=keyFieldOne
// +k8s:listMapKey=keyFieldTwo
type MyList []MyStruct
type MyStruct struct {
keyFieldOne string `json:"keyFieldOne"`
keyFieldTwo string `json:"keyFieldTwo"`
valueField string `json:"valueField"`
}
У цьому прикладі listMapKey використовується для вказівки, що keyField типу MyStruct повинен використовуватися як ключ для list-map.
+k8s:listType
Опис:
Оголошує семантичний тип поля списку. Цей теґ використовується для вказівки того, як список повинен оброблятися, наприклад, як map або set (набір).
Корисне навантаження:
atomic: Список обробляється як одне атомарне значення.map: Список обробляється як map, де кожен елемент має унікальний ключ. Вимагає використання+k8s:listMapKey.set: Список обробляється як set (набір), де кожен елемент є унікальним.
Приклад використання:
// +k8s:listType=map
// +k8s:listMapKey=keyField
type MyList []MyStruct
type MyStruct struct {
keyField string `json:"keyField"`
valueField string `json:"valueField"`
}
У цьому прикладі MyList оголошується як список типу map, з keyField як ключем. Це означає, що логіка валідації забезпечить те, що кожен елемент списку має унікальний keyField.
+k8s:maxItems
Опис:
Вказує, що поле списку має обмеження на розмір.
Корисне навантаження:
<негативне число>: Це поле не повинно містити більше X елементів.
Приклад використання:
type MyStruct struct {
// +k8s:maxItems=5
MyList []string `json:"myList"`
}
У цьому прикладі MyList не може містити більше 5 елементів.
+k8s:maxLength
Опис:
Вказує, що рядкове поле має обмеження на довжину.
Корисне навантаження:
<негативне число>: Це поле не повинно містити більше X символів.
Приклад використання:
type MyStruct struct {
// +k8s:maxLength=10
MyString string `json:"myString"`
}
У цьому прикладі MyString не може бути довшим за 10 символів.
+k8s:minimum
Опис:
Вказує, що числове поле має мінімальне значення.
Корисне навантаження:
<ціле число>: Це поле повинно бути більшим або рівним x.
Приклад використання:
type MyStruct struct {
// +k8s:minimum=0
MyInt int `json:"myInt"`
}
У цьому прикладі MyInt повинен бути більшим або рівним 0.
+k8s:neq
Опис:
Перевіряє, що значення поля не дорівнює конкретному забороненому значенню. Підтримує рядкові, цілі та булеві типи.
Корисне навантаження:
<значення>: Заборонене значення. Парсер вгадає тип (рядок, int, bool).
Приклад використання:
type MyStruct struct {
// +k8s:neq="disallowed"
MyString string `json:"myString"`
// +k8s:neq=0
MyInt int `json:"myInt"`
// +k8s:neq=true
MyBool bool `json:"myBool"`
}
У цьому прикладі:
MyStringне може дорівнювати"disallowed".MyIntне може дорівнювати0.MyBoolне може дорівнюватиtrue.
+k8s:opaqueType
Опис:
Вказує, що будь-які валідації, оголошені для вказаного типу, будуть проігноровані. Якщо пакет вказаного типу не включено в поточні прапорці генератора, цей теґ повинен бути встановлений, інакше генерація коду завершиться невдачею (запобігаючи тихим помилкам). Якщо валідації не повинні ігноруватися, додайте пакет типу до генератора за допомогою прапорця --readonly-pkg.
Приклад використання:
import "some/external/package"
type MyStruct struct {
// +k8s:opaqueType
ExternalField package.ExternalType `json:"externalField"`
}
У цьому прикладі будь-які теґи валідації на package.ExternalType будуть проігноровані.
+k8s:optional
Опис:
Вказує, що поле є необовʼязковим для клієнтів.
Приклад використання:
type MyStruct struct {
// +k8s:optional
MyField string `json:"myField"`
}
У цьому прикладі MyField не є обовʼязковим для надання під час створення або оновлення MyStruct.
+k8s:required
Опис:
Вказує, що поле має бути вказане клієнтами.
Приклад використання:
type MyStruct struct {
// +k8s:required
MyField string `json:"myField"`
}
У цьому прикладі MyField повинно бути надане під час створення або оновлення MyStruct.
+k8s:subfield
Опис:
Оголошує валідацію для підполя структури.
Аргументи:
<field-json-name>(рядок, обовʼязково): Імʼя JSON підполя.
Корисне навантаження:
<validation-tag>: Теґ для оцінки для підполя.
Приклад використання:
type MyStruct struct {
// +k8s:subfield("mySubfield")=+k8s:required
MyStruct MyStruct `json:"MyStruct"`
}
type MyStruct struct {
MySubfield string `json:"mySubfield"`
}
У цьому прикладі MySubfield всередині MyStruct є обовʼязковим.
+k8s:supportsSubresource
Опис:
Теґ +k8s:supportsSubresource є коментарем на рівні пакета, який оголошує, які субресурси є дійсними цілями для валідації для типів у межах цього пакета. Розглядайте цей теґ як реєстрацію точки доступу; він говорить системі валідації, що конкретний шлях субресурсу визнано і його не слід відхиляти відразу.
Коли генерується код валідації, цей теґ додає вказаний шлях субресурсу до основної функції диспетчера для типу. Це дозволяє вхідним запитам для цього субресурсу бути надісланими до реалізації валідації.
Кілька теґів можуть використовуватися для оголошення підтримки кількох субресурсів. Якщо в пакеті немає жодного теґа +k8s:supportsSubresource, валідація увімкнена лише для кореневого ресурсу (наприклад, .../myresources/myobject), і будь-які запити до субресурсів будуть відхилені з помилкою "валідація не знайдена".
Окрема використання:
Якщо ви використовуєте +k8s:supportsSubresource без відповідного теґа +k8s:isSubresource для конкретної валідації, стандартно правила валідації для кореневого обʼєкта будуть застосовані до субресурсу.
Область дії: Пакет
Корисне навантаження:
<subresource-path>: Шлях субресурсу, який потрібно підтримувати (наприклад,"/status","/scale").
Приклад використання:
Додавши ці теґи, ви дозволяєте системі валідації обробляти запити для /status та /scale субресурсів для типів, визначених у пакеті v1.
Файл: staging/src/k8s.io/api/core/v1/doc.go
// +k8s:supportsSubresource="/status"
// +k8s:supportsSubresource="/scale"
package v1
+k8s:unionDiscriminator
Опис:
Вказує, що це поле є дискримінатором для обʼєднання.
Аргументи:
union(рядок, необовʼязково): Імʼя обʼєднання, якщо їх кілька.
Приклад використання:
type MyStruct struct {
TypeMeta int
// +k8s:unionDiscriminator
D D `json:"d"`
// +k8s:unionMember
// +k8s:optional
M1 *M1 `json:"m1"`
// +k8s:unionMember
// +k8s:optional
M2 *M2 `json:"m2"`
}
type D string
const (
DM1 D = "M1"
DM2 D = "M2"
)
type M1 struct{}
type M2 struct{}
У цьому прикладі поле Type є дискримінатором для обʼєднання. Значення Type визначить, який з членів обʼєднання (M1 або M2) очікується наявним.
+k8s:unionMember
Опис:
Вказує, що це поле є членом обʼєднання.
Аргументи:
union(рядок, необовʼязково): Імʼя обʼєднання, якщо їх кілька.memberName(рядок, необовʼязково): Значення дискримінатора для цього члена. Стандартно використовується імʼя поля.
Приклад використання:
type MyStruct struct {
// +k8s:unionMember(union: "union1")
// +k8s:optional
M1 *M1 `json:"u1m1"`
// +k8s:unionMember(union: "union1")
// +k8s:optional
M2 *M2 `json:"u1m2"`
}
type M1 struct{}
type M2 struct{}
У цьому прикладі M1 і M2 є членами названого обʼєднання union1.
+k8s:zeroOrOneOfMember
Опис:
Вказує, що це поле є членом групи нуль-або-один-з. Група нуль-або-один-з дозволяє встановити не більше ніж одного члена. На відміну від звичайних обʼєднань, відсутність встановлених членів є дійсною.
Аргументи:
union(рядок, необовʼязково): Імʼя обʼєднання, якщо їх кілька.memberName(рядок, необовʼязково): Користувацьке імʼя члена для цього члена. Стандартно використовується імʼя поля.
Приклад використання:
type MyStruct struct {
// +k8s:zeroOrOneOfMember
// +k8s:optional
M1 *M1 `json:"m1"`
// +k8s:zeroOrOneOfMember
// +k8s:optional
M2 *M2 `json:"m2"`
}
type M1 struct{}
type M2 struct{}
У цьому прикладі не більше ніж один з A або B може бути встановлений. Також дійсно, що жоден з них не встановлений.