Апгрейдим Drupal 6 + PostgreSQL до Drupal 7 в 32 простых шага.

Пишу чеклист для себя, но вдруг кому еще пригодится.

Проверенная на себе процедура апгрейда с D6 на D7 в общих чертах соответствует тому, что рекомендуется на drupal.org, но моя склонность к PostgreSQL ее несколько разнообразит.

Мне "повезло" с модулями (точнее, я дождался, пока все реально мне нужное перенесут на D7), поэтому совсем нестандартные шаги потребовались только с картинками.

Мотивация

Лично у меня, по состоянию на вчера, 3 сайта работали на D7, а еще пять - на D6. По результатам полугода эксплуатации, семерка лучше:
  • Версия 7.15 вполне стабильна (я начал с 7.11 и проблем не помню).
  • Гораздо меньше проблем с PostgreSQL, чем в D6.
  • Многие модули для D6 выглядят уже неживыми, а для семерки - поживее. Вот, например fbconnect.
  • Ну и вообще, иметь в управлении две версии - это иметь двойной геморой.

Предварительные шаги

  1. Копируем БД в другую базу:
    createdb new.site.ru
    pg_dump -F c old.site.ru | pg_restore -d new.site.ru
  2. Копируем весь контент сайта в другой каталог.
    cd /path/to/my/sites
    cp -Rp oldsite newsite
  3. Настраиваем nginx (или что у вас там), чтобы new.site.ru запускался из /path/to/my/sites/newsite
  4. Редактируем sites/mysite.ru/settings.php:
    • База данных: new.site.ru
    • Префикс memcached (если используется кэширование в memcached) - отличающися от old.site.ru
  5. Заводим в DNS new.site.ru
После этого у нас по http://new.site.ru должен открываться сайт, по контенту совпадающий со старым, но новый. Можно там чего-нибудь поделать, чтобы убедиться что на http://www.site.ru ничего не меняется.

Апгрейд базовых модулей

  1. Отключаем все 3rd-party modules (в /admin/build/modules)
  2. Ставим стандартный дизайн Garland
  3. Удаляем старый код:
    cd /path/to/my/sites/new.site.ru
    rm * .htaccess # all files
    rm -fr includes misc modules profiles scripts themes # drupal distro
    rm -fr sites/all/modules/* sites/all/themes/* # 3rd party
  4. Накатываем патчи, чтобы апгрейд с постгресом проходил:
    • Преобразование типов (bytea->text) берем из этого поста
    • Патч LIKE/ILIKE, накатывается на Drupal:
      --- a/includes/database/pgsql/database.inc
      +++ b/includes/database/pgsql/database.inc
      @@ -74,6 +74,17 @@ class DatabaseConnection_pgsql extends DatabaseConnection {
           }
         }
       
      +  public function prepareQuery($query) {
      +    // mapConditionOperator converts LIKE operations to ILIKE for consistency
      +    // with MySQL. However, Postgres does not support ILIKE on bytea (blobs)
      +    // fields.
      +    // To make the ILIKE operator work, we type-cast bytea fields into text.
      +    // @todo This workaround only affects bytea fields, but the involved field
      +    //   types involved in the query are unknown, so there is no way to
      +    //   conditionally execute this for affected queries only.
      +    return parent::prepareQuery(preg_replace('/ ([^ ]+) +(I*LIKE|NOT +I*LIKE) /i', ' ${1}::text ${2} ', $query));
      +  }
      +
         public function query($query, array $args = array(), $options = array()) {
       
           $options += $this->defaultOptions();
    • Патч для апгрейда таксономий (безумно странное место, но патч работает):
      *** a/modules/taxonomy/taxonomy.install.orig    Mon Oct  1 15:45:16 2012
      --- a/modules/taxonomy/taxonomy.install Mon Oct  1 15:45:55 2012
      ***************
      *** 682,687 ****
      --- 682,692 ----
            $query->orderBy('tn.vid');
            $query->orderBy('td.weight');
            $query->orderBy('tn.tid');
      +
      +     $fields =& $query->getFields();
      +     unset($fields['td.weight']);
      +     unset($fields['tn.tid']);
      +
            db_insert('taxonomy_update_7005')
              ->from($query)
              ->execute();
  5. Преобразуем [ img_assist в <img src скриптом из этого поста
  6. Убираем из settings.php все настройки memcached (если они там были)
  7. chmod 666 settings.php
  8. В settings.php лучше поставить upgrade_free_access=true, потому что авторизоваться вам пока негде.
  9. Запускаем new.site.ru/update.php, оно должно сказать про ~160 pending patches.
    Соглашаемся, процесс накатки патчей должен пройти без ошибок. Если ошибки есть - гуглим их, находим патчи. Все патчи, которые потребовались мне - показаны выше.
После этого у нас должен получиться сайт, на котором есть весь старый контент, скорее всего провисли пункты меню, нету Views, нету Image Nodes, стандартный Garland-дизайн и еще немного по мелочи.

Ускоренный вариант шагов 1-14

Для крепких духом и не боящихся слегка разваленного сайта, ускоренный вариант:
  1. Копируем БД в другую базу (шаг 1 в предыдущем варианте)
  2. Патчим новую базу (патч для bytea - text и преобразование картинок)
  3. Разворачиваем drupal-7.x в /path/to/sites/newsite/. Только дистрибутив, никаких дополнительных модулей.
  4. Патчим друпал (шаг 9 в предыдущем варианте)
  5. Копируем содержимое сайта в новый каталог:
    sudo cp -Rp oldsite/sites/mysite/ newsite/sites/
  6. Правим newsite/sites/mysite/settings.php:
    • Убираем все про memcached
    • Меняем db_url, чтобы указывал на новую базу.
    • chmod 666
  7. Запускаем new.site.ru/update.php (у меня немного поругалось на locale), убеждаемся что ~160 патчей проходят без ошибок.
  8. Логинимся на new.site.ru/user/login (скорее всего там все выглядит ужасно, но залогиниться можно)
  9. Заходим на new.site.ru/admin/appearance, выбираем Garaland - Set Default
  10. Чистим кэш new.site.ru/admin/config/development/performance - должен получится вполне рабочий Garaland, но без админских линков в меню: старые неверны, а новые не добавили.
  11. Заходим на new.site.ru/admin/modules, включаем Toolbar
  12. Чистим тулбарное меню (см. последний пункт в самом конце поста)
В результате мы получаем все то же самое, что и первым способом (пункты 1-14), но
  • Без лишних копирований-стираний
  • Ценой временного развала дизайна
  • Если где-то дальше ошибемся, можно еще раз поднять базу из бэкапа, запатчить ее и перейти сразу к update.php.

Приведение в чувство

  1. Ставим нужные модули для D7 обычным способом (распаковкой в sites/all/modules)
  2. Запускаем new.site.ru/update.php, апдейтим БД для этих модулей. Для моего списка модулей это прошло без звука.
  3. Редактируем E-mail notifications для сообщений о заведении аккаунта, восстановлении пароля. Переменные поменялись, проще скопировать с готового D7-сайта.
  4. Ставим таймзоны в /admin/config/regional/settings
  5. Проверяем ВСЕ Views:
    • Views по комментариям - у меня гарантированно разваливались все, апгрейд их проходит как-то неверно. Проще их пересоздать.
    • Views по контенту - "как повезет", т.е. сами выборки оставались вроде верные, а вот настройки листалок, количества записей - временами слетают, системы не уловил.
  6. В D7 добавился новый, неотключаемый тип текста 'Plain Text'. Мне он не нужен, я делаю ему такие же настройки, как Filtered HTML, а Filtered HTML - разрешаю только администраторам.
    Удалять ненужные текстовые типы - опасно (на эти грабли было наступлено!), удалится (перестанет показываться) контент с этими типами. Например, комментарии (заголовок есть, текста нет). Это тоже разруливается, но одним запросом к БД (который сменит format всем записям) не обойтись, т.к. комментарии - это не отдельная таблица в БД, а сделаны теперь через Fields.
  7. По-умолчанию, таксономии показываются вместе с контентом. Там где это не нужно - нужно отключить (Structure - Content Types - Manage display), отдельно для показа ноды, отдельно для teaser view.

Конверсия Image nodes

На страницы с картинками (image nodes) могут быть внешние ссылки, поэтому если хочется их оставить, то выполняется процедура описанная здесь
  1. Ставим модули Field Convert и image_legacy
  2. Запускаем процедуру конвертирования http://new.site.ru/admin/content/field_convert
  3. Появляется новый Content Type Image, появляются записи этого типа, URL старые, все работает.
  4. Деинсталлируем image_legacy и Field Convert, они больше не нужны

Финальные штрихи

  1. Ставим выбранный дизайн
  2. Распихиваем блоки по странице:
    • Если блок показывается не на всех страницах, то эта информация осталась (сохранилась при апгрейде), но прячется где-то в кэшах. Нужно в каждом таком блоке нажать Configure, а потом Save
  3. Внимательно проверяем Permissions:
    • Система прав несколько изменилась, поэтому всю простыню с правами нужно (обязательно!) прочитать и поставить/снять правильные галочки. Не сделавший этого - будет наказан.
  4. Восстанавливаем memcached (настройки в settings.php), лучше - с новым префиксом, дабы из варианта со старым дизайном ничего не налезло.
  5. Тестируемся на предмет висящих ссылок и вообще работоспособности.
Можно запускать:
  1. Переконфигурируем nginx, чтобы new.site.ru стал www.site.ru, а старый вариант - old.site.ru
  2. Чтобы заработал админский toolbar: delete from menu_links where module='system';, а затем почистить кэш. Правильные ссылки в toolbar-menu заведутся после этого сами.

Итого

После тренировки на кошках (на локальном сервере, для скорости) вся процедура для маленького сайта занимает час или около того. Но чеклист - нужен, для того и пишу. Без чеклиста очень легко забыть, к примеру, шаг 10, обнаружить это на шаге 15 и... начать все сначала, потому что структура БД изменилась и откатиться некуда.

Comments

zfs snapshot перед каждым серьёзным пунктом может сильно помочь ;)

Ну я как-то сильно в этом сомневаюсь.
То есть файлы я, понятно, откачу (это и так не фокус), а с базой что делать? Если откатить только файлы с таблицами, то им, поди, из лога накатят все изменения после рестарта базы.

Т.е. надо отдельную копию сервера БД и откатывать и таблички и лог. Как-то это неудобно очень.

Не-не, снапшот-то (если ты только специально не выносил WAL на отдельную файловую систему) будет вполне консистентным, и после рестарта (да, перед роллбэком, разумеется, базу надо остановить, и пустить после) накатятся как раз все завершённые транзакции на момент снапшота.

Но при этом у меня уедут назад и остальные базы - а этого мне не надо.