Блог ·
Слово можливості для старого наказу
Коли раунд додає поле protobuf, старий агент падає гучно. Коли він лише змінює значення позначки, той самий агент відповідає правдоподібно і хибно.
Автор: Provibr
Наш агент працює всередині власної мережі клієнта і додзвонюється назовні до нас. Він оновлює себе, але не миттєво і не завжди, тож будь-якої миті флот є розкидом версій, і платформа мусить знати, що вміє кожна з них.
Вона знає, бо агент їй каже. На кожній сесії агент надсилає перелік коротких слів, по одному на можливість, і платформа відмовляє наказові, чийого слова бракує, замість надіслати його й сподіватися.
Цей раунд додав третє таке слово для контейнерів Proxmox, і воно є першим, де цікавим питанням було не те, чи його додавати, а те, що сталося б без нього.
Навіщо ці слова взагалі існують
Механізм захищає від однієї конкретної властивості protobuf: невідоме поле в oneof відкидають.
Коли раунд додає новий наказ як нове поле, агент, старіший за той раунд, розбирає повідомлення, не впізнає номера поля, викидає його і лишається з ProxmoxCommand без наказу всередині. Тоді він каже рівно це. Повідомлення є чесним і цілком некорисним: оператор бачить скаргу на порожній конверт, а причина лежить за чотири шари звідти, у розбіжності версій, про яку ніхто не згадав.
Тож платформа відмовляє першою. Якщо слова бракує, наказ ніколи не вирушає, а відмова називає можливість і версію агента. Це є гіршим наслідком, ніж наказ, що спрацював, і значно кращим, ніж наказ, що падає у спосіб, на простеження якого йде ціле пообіддя.
У будь-якому разі це є гучною відмовою. Виявляється, що саме це має значення.
Третє слово не є новим полем
Зміною цього раунду було те, що контейнер тепер може показати своє обладнання. Наказ, який це робить, GetHardware, не є новим. Кожен агент аж до ранніх робіт над життєвим циклом віртуальних машин знає це повідомлення і знає, як на нього відповісти.
Змінилася позначка поруч із ним, що каже, до якого роду ресурсу належить ідентифікатор. Агент, старіший за цей раунд, отримує ту позначку, не знає її і викидає, рівно так, як каже protobuf. Тоді він робить те, що завжди робив із запитом про обладнання: питає в Proxmox конфігурацію машини на шляху QEMU.
Ніщо не є порожнім. Ніщо не скаржиться. Агент вважає, що йому поставили питання, яке він розуміє, і відповідає.
Є два способи, якими це приземляється, і жоден із них не є гучним. Якщо в Proxmox за тим ідентифікатором на шляху віртуальної машини немає нічого, клієнтові кажуть, що машини не існує, про контейнер, який працює просто перед ним. А якщо конфігурація таки повертається і потрапляє до парсера, побудованого для віртуальних машин, відповідь є не порожньою, вона є хибною.
Хибна відповідь є конкретною, і вона має добрий вигляд
Два формати конфігурації є досить близькими, щоб розібратися, і досить різними, щоб це важило. Віртуальна машина записує свою першу мережеву карту як модель і MAC-адресу, зʼєднані знаком рівності. Контейнер записує спершу імʼя інтерфейсу, а тоді MAC в окремому полі з ключем.
Парсер для віртуальних машин бере першу пару ключа і значення як модель і адресу. Тож, отримавши рядок контейнера, він повідомляє про мережеву карту, чиєю моделлю є name, а чиєю MAC-адресою є ETH0.
Це є правдоподібним на вигляд рядком у таблиці. У нього є модель, у нього є адреса, його малюють без застереження. Саме тому шляхові контейнерів знадобився власний парсер, і це є рівно тим класом дефектів, про які ніхто не повідомляє, бо в них немає про що повідомляти. Хтось дивиться на екран, думає, що стовпець моделі якийсь дивний, і йде далі.
Тож наказ, що вже існує, є небезпечнішим родом зміни, ніж наказ, що є новим. Новий падає на конверті, привселюдно. Цей падає на значенні, потайки.
Чому нічого не розширювати, а додати один рядок
Очевидною альтернативою є розширити слово, яке вже існує. Є слово, що каже, що агент узагалі розуміє шлях контейнерів, і роботи на один символ вистачило б, щоб воно означало ще й новішу річ.
Це відмовляло б кожному наказові про контейнер від кожного агента, який ще не оновився, зокрема й тим, які сьогодні працюють бездоганно. Флот, що зараз обслуговує контейнери, згас би для можливості, яку він справді має, бо ми змінили значення нашого слова в нього під ногами.
Слово є обіцянкою про те, що збудовано, і обіцянка мусить тримати своє значення доти, доки хтось її ще тримає. Додати третє слово коштує однієї сталої з кожного боку і відмовляє рівно тим наказам, яким треба відмовити, і більше нічому. Воно ще й кладе міркування туди, де людина його знайде, тобто поруч зі сталою, а не в повідомленні комміту.
Двома боками є дві сталі, одна в джерелі мовою Rust і одна у спільному TypeScript, кожна з яких називає іншу. Ця пара не є елегантною, і кожна спроба зробити її елегантною закінчується кроком збірки між двома мовами, які мають читатися незалежно.
Заглушка каже два з трьох навмисно
Є агент-заглушка, що дає нам керувати шлюзом без гіпервізора, і він теж оголошує слова контейнерів.
Він оголошує два з них. Він каже, що знає шлях контейнерів, і каже, що читає обладнання контейнера, і навмисно не заявляє третього, бо перевстановити контейнер він не може.
Це варто сказати, бо заглушка, що заявляє все, є заглушкою, яка робить кожен шлях відмови невипробовуваним. І протилежне важить рівно стільки ж: без слова про обладнання шлюз відмовив би, і слушно, а перевірка тихо зміряла б відмову замість відповіді. Випробувальна оснастка, що бреше про власні можливості, дає зелені результати про не ту річ.
Що ми б записали
- Protobuf викидає невідомі поля мовчки, тож новий наказ доходить до старого агента порожнім конвертом.
- Відмовляйте, перш ніж надсилати. Відмова, що називає можливість і версію, є кращою за скаргу з чотирьох шарів нижче.
- Небезпечною є зміна без нового поля. Ніщо не викидають, тож ніщо не скаржиться.
- Спосіб відмови, якого ви хочете, є гучним. Про правдоподібну відповідь, яка є хибною, вам ніколи не повідомлять.
- Два формати конфігурації, що обидва розбираються, є гіршими за два, які ні.
- Ніколи не розширюйте наявного слова можливості. Це є обіцянкою, яку тримають агенти, до яких вам не дістатися.
- Одне зайве слово коштує однієї сталої з кожного боку, і відмовляє лише тому, чому треба відмовити.
- Кладіть міркування поруч зі сталою, там, де насправді стоятиме наступна людина.
- Заглушка мусить відмовлятися від того, чого не може, інакше кожен шлях відмови стає невипробовуваним.
- Оснастка, що заявляє забагато, дає зелені результати про не ту річ.
Для ясності про те, чим це є: третє слово можливості для контейнерів Proxmox, додане поряд зі зміною, яка не ввела жодного нового поля протоколу, з міркуваннями, записаними при обох сталих, і агентом-заглушкою, що оголошує два з трьох; раунд посунув тестовий модуль контейнерів із тридцяти пʼяти тестів до тридцяти девʼяти, з чистими форматуванням і цільовою платформою релізу, і завершився спільними пакунками зеленими на 5347, шлюзом на 1467, інструментами-заглушками на 489 і набором вебу на 4593, без жодної невдачі, і ще пʼятдесятьма браузерними перевірками зеленими двома мовами. Не доведено: ніщо тут не запускали проти справжнього Proxmox, тож поведінку старішого агента на ідентифікаторі контейнера встановлено з двох форматів конфігурації і парсера, а не спостережено, а ланцюг у дії випробувано проти нашої власної заглушки.
Із платформної роботи над Provibr. Що стається, коли два раунди тягнуться по той самий номер поля, описано в Два раунди, один номер поля, раунд, що вперше навчив агента про контейнери, описано в Перевстановлення контейнера є заміною, а те, як агент узагалі вирішує, чи довіряти новій збірці, описано в Як підписане оновлення агента себе відхиляє.