Історія одного бага (#1653967)

Abstract: Реальна історія з життя реальних адміністраторів по вилову ідіотського бага.
Повчальна частина: Ніколи недооценивай залежності залежностей.

Вступ
Рядовий апгрейд в лабораторії з Openstack Mitaka до Openstack Newton (нова версія). Кілька deprecated options у файлах конфігурації, keystone переїхав з eventlet на WSGI і поламав існуючу конфігурацію з haproxy; типового «ipv6 listen» apache не почав конфліктувати з haproxy за однакові використовувані порти на зірці (один слухав ipv6, інший ipv4 only), так що запити йшли в haproxy замість апача, де вмирали з 503, т. к. апстрима не було… Втім, історія не про це.

Після того, як основні проблеми були пофишкены, Nova (одна з компонент Openstack) при запуску почала падати з помилкою:
ConfigFileValueError: Value for option url is not valid: invalid URI: 'http://neutron-server.example.com:21345'.
. Це було дуже дивно. З урахуванням, що в конфіги змінилося 100500 опцій, виникла підозра, що ми використовуємо застарілу опцію, яку більше не треба використовувати. Однак, документація говорила, що приклад опції —
url = http://controller:9696
.

Налагодження
Очевидні кроки відладки:
  • Закоментувати опцію — не падає
  • Повторити опцію приклад — не падає
  • Замінити в опції порт на «наш» — можливо, не можна використовувати надто великий номер порту — не падає
  • Замінити в опції url на наш падає
  • Повернути «controller» на місце — не падає
  • Підозра: чи не вміє fqdn: замінити controller на controller.dns — не падає
  • Підозра: занадто багато точок (у нас в реальному коді було 8 точок в url) — controller.dns1.dns2.dns3.dns4 — не падає
  • Залишити з нашого імені лише першу частину:
    http://neutron-server:9696
    — падає! гіпотеза вже зрозуміла.
  • Проверка1:
    http://neutronserver:9696
    — не падає
  • Проверка2:
    http://with-dashes:9696
    — падає!

Отже, бага: наявність дефіса в hostname викликає ConfigFileValueError. Баг репортится: bugs.launchpad.net/ubuntu/+source/nova/+bug/1653967

Перевірка, що це баг: RFC3986 утвержает, що:

unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
reg-name = *( unreserved / pct-encoded / sub-delims )
host = IP-literal / IPv4address / reg-name

(це така bnf-нотація, коли говорить, що в host може бути "-").

Тобто дефіс може бути в hostname при роботі з доменними іменами. Ми це все знаємо, але завжди краще перевірити.

Вивчення коду, який повідомляє про помилку:

try:
return convert(opt._get_from_namespace(namespace, group_name))
except KeyError: # nosec: Valid flow control instruction
pass
except ValueError as ve:
raise ConfigFileValueError(
"Value for option %s is not valid: %s"
% (opt.name, str(ve)))

Помилка виникала на двох опціях: url і novncproxy_base_url. Помилка ідентична, хоча grep'ать зручніше за другим. Починаємо шукати друга. Ось як вона визначається в коді:

cfg.URIOpt(
'novncproxy_base_url',
default='http://127.0.0.1:6080/vnc_auto.html',
deprecated_group='DEFAULT',
help="""

Ага. А cfg — це
from oslo_config import cfg
. oslo.config — це бібліотека Openstack для роботи з конфіг. Дивимося сырцы.

Бачимо:

class URI(ConfigType):

def __call__(self, value):
if not rfc3986.is_valid_uri(value, require_scheme=True,
require_authority=True):
raise ValueError('invalid URI: %r' % value)

Раптово:

>>> import rfc3986
>>> rfc3986.is_valid_uri('http://test.com')
True
>>> rfc3986.is_valid_uri('http://test-test.com')
False

Упс. Непорядок. Зате є: github.com/sigmavirus24/rfc3986/issues/11
Бага давно пофикшена. У версії 0.2.2. А на хості у нас:

apt-cache policy python-rfc3986
python-rfc3986:
Installed: 0.2.0-2
Candidate: 0.2.0-2
Version table:
*** 0.2.0-2 500
500 archive.ubuntu.com/ubuntu xenial/main amd64 Packages
100 /var/lib/dpkg/status

Зате в більш свіжої версії в zesty є версія 0.3.1-2, яка такою проблемою не страждає.

Подальший розгляд
Давним-давно був зроблений Баг. Він був деякий час, потім його пофиксили. Але за цей час запакетировали Код, в якому був Баг, і ніхто не звернув увагу на Фікс Бага, і версія з Багом залишалася в deb-репозиторії роками. Вона нікого не хвилювала — поки не сталися два коміта в oslo.config і nova:

commit 45ee2bed52a57b9801435b43ad45d8f50204580d
Author: Masaki Matsushita <glass.saga@gmail.com>
Date: Mon Sep 28 20:28:28 2015 +0900

Add URIOpt

Add This change URIOpt which validates string as URI.

Closes-Bug: #1500398
Change-Id: Ie8736b8654b9feb2a2b174159f08dbea03568d84

commit 6091de77eda12286786e28ae4f0779e7efc54634
Author: Maciej Szankin <maciej.szankin@intel.com>
Date: Thu Jul 28 10:30:59 2016 -0500

Improve consistency in VNC opts

* Updated header flags
* Moved all vars to list
* Removed possible values and related options sections where they were not
needed
* Changed IntOpt to PortOpt where needed

Change-Id: I3255a867091f8e14c907c7fde9a2aa3abc249ae9
Implements: Blueprint centralize-config-options-newton

Цей комміт зробив з StrOpt UriOpt і почав використовувати (через oslo.conf) python-rfc3986. З-за того, що була запакетирована стара опція, в програмному ПО виникла несподівана регресія.

Бонусне: як ми це будемо фиксить
Зазвичай в таких випадках, якщо апгрейд на більш нову версію дається легко (і не викликає інших проблем), то ми просто забираємо пакет з більш нової версії дистрибутива (в даному випадку — ще не вийшла zesty, ака ubuntu-17.04). Ми покладемо його в наш приватний репозиторій під управлінням aptly (як є) і будемо його використовувати при встановленні/налаштування сервером. Якщо б такого пакету не було в природі — ми налаштували б на CI джобу на його пакетування та публікацію (в репозиторій aptly). Якщо цей варіант не був доступний (наприклад, несумісні зміни), то ми б додали в наш patchqueue для nova ще один патч, який би робив StrOpt замість UriOpt. Це означає, що ми будемо перезбирати nova з ubuntu-пакета з нашими власними патчами. Цим займається CI, який публікує пакети у той самий наш приватний репозиторій.

Трохи флейму
І як би ця проблема вирішувалася в пропрієтарної середовищі? Помилки роблять всі (інакше б у нас було без багів). Після того, як помилка була зарепочена в саппорт першого рівня, і після боданий про встановлені версії, оновлення та контракти, воно дійшло б до саппорта другого рівня, третього рівня, і так аж до людини з реальною кваліфікацією, він би знайшов і виправив проблему. Який estimate для того баг-фікса? Дві години на перший рівень, ще годину на другий, business day на дослідження проблеми, ще один business day на фікс, можливо ще один день на реліз і тестування. Це ідеальний сценарій. На практиці, мої найоптимістичніші оцінки говорять про тижні, перетворюються в «наступному релізі через пів-року поправили».

Скільки це зайняло у мене, в opensource проект? ~14:30, сьогодні проблема виявилася, і я зарепортил його на launchpad. О 15:20 вже було відомо про проблеми з залежністю, о 15:30 було перевірено, що з новою версією python-rfc3986 цієї проблеми немає. О 16:50 (за кіпрському часу) я закінчую писати цей пост на Хабр.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.