Введение
Итак, вы разработали своё приложение на технологиях InterSystems. Теперь его надо развернуть у клиента и часто не один раз. В процессе разработки у вас появилась инструкция по установке – потому что недостаточно просто импортировать классы, нужно ещё и
Для решения этих задач существует утилита %Installer. Эта статья о ней.
%Installer
Эта утилита позволяет определить манифест установки, который описывает целевую конфигурацию Cache, а не шаги к его достижению. Вы описываете, что должно быть, а Cache уже сгенерирует весь необходимый для этого код. Распространяете вы, соответственно, только сам манифест, кодогенерация происходит уже на месте в рамках конкретного сервера Cache.
Для написания манифеста вы создаёте блок XData с описанием желаемой конфигурации, а также метод, который генерирует COS код на основе блока XData (он всегда одинаков). После того, как вы написали манифест, его можно вызвать во время инсталляции Cache, из терминала или COS-кода. Манифест должен исполняться в области %SYS. Манифесту доступны как параметры системы (супер-порт, ОС, mgr директория и др.), так и произвольные параметры, переданные пользователем. Таким образом, класс установщика должен отвечать следующим требованиям:
- Включать ссылку на
%occInclude.inc
; - Содержать блок
XData
с конфигурацией сервера Cache; - Блок может называться любым корректным именем
- Добавьте
[XMLNamespace = INSTALLER]
после имени блока для включения подсказок в студии; - Корневой элемент (может быть только один) –
<Manifest>
, все остальные элементы расположены внутри него; - Необходимо определить метод
setup()
, который выполняет кодогенерацию из блока XData.
Использование
Готовый манифест установки может быть запущен несколькими способами:
- В области %SYS из терминала или COS кода
do ##class(MyPackage.MyInstaller).setup()
- Автоматически при установке Cache. Для этого экспортируйте класс установщика в файл DefaultInstallerClass.xml в папку с дистрибутивом Cache (где находится setup_cache.exe, или cinstall). При установке Cache он будет импортирован в область %SYS и запущен (через метод setup())
Пример
Рассмотрим простой пример. Создадим класс
App.Installer
c установщиком, который создаёт область с названием, переданным пользователем:App.Installer
Include %occInclude
Class App.Installer
{
/// Сгенерированный код можно посмотреть в zsetup+1^App.Installer.1
XData Install [ XMLNamespace = INSTALLER ]
{
<Manifest>
<If Condition='(##class(Config.Namespaces).Exists("${Namespace}")=0)'>
<Log Text="Creating namespace ${Namespace}" Level="0"/>
<Namespace Name="${Namespace}" Create="yes" Code="${Namespace}" Ensemble="0" Data="${Namespace}">
<Configuration>
<Database Name="${Namespace}" Dir="${MGRDIR}/${Namespace}" Create="yes"/>
</Configuration>
</Namespace>
<Log Text="End Creating namespace ${Namespace}" Level="0"/>
</If>
</Manifest>
}
/// Метод, который вызывается для выполнения установки.
/// Во время компиляции класса происходит генерация COS кода
/// После этого установщик запускается с параметрами следующим образом:
/// Set pVars("Namespace")="TempNamespace"
/// Do ##class(App.Installer).setup(.pVars)
ClassMethod setup(ByRef pVars, pLogLevel As %Integer = 0, pInstaller As %Installer.Installer) As %Status [ CodeMode = objectgenerator, Internal ]
{
Quit ##class(%Installer.Manifest).%Generate(%compiledclass, %code, "Install")
}
}
Class App.Installer
{
/// Сгенерированный код можно посмотреть в zsetup+1^App.Installer.1
XData Install [ XMLNamespace = INSTALLER ]
{
<Manifest>
<If Condition='(##class(Config.Namespaces).Exists("${Namespace}")=0)'>
<Log Text="Creating namespace ${Namespace}" Level="0"/>
<Namespace Name="${Namespace}" Create="yes" Code="${Namespace}" Ensemble="0" Data="${Namespace}">
<Configuration>
<Database Name="${Namespace}" Dir="${MGRDIR}/${Namespace}" Create="yes"/>
</Configuration>
</Namespace>
<Log Text="End Creating namespace ${Namespace}" Level="0"/>
</If>
</Manifest>
}
/// Метод, который вызывается для выполнения установки.
/// Во время компиляции класса происходит генерация COS кода
/// После этого установщик запускается с параметрами следующим образом:
/// Set pVars("Namespace")="TempNamespace"
/// Do ##class(App.Installer).setup(.pVars)
ClassMethod setup(ByRef pVars, pLogLevel As %Integer = 0, pInstaller As %Installer.Installer) As %Status [ CodeMode = objectgenerator, Internal ]
{
Quit ##class(%Installer.Manifest).%Generate(%compiledclass, %code, "Install")
}
}
В этом примере происходит следующее:
- Проверяем, существует ли область с именем равным значению переменной Namespace (например, значение равно NewNamespace);
- Если нет, то логируется факт начала создания области NewNamespace;
- Определение области:
- C названием NewNamespace;
- Область нужно создать;
- БД кода NewNamespace;
- Ensemble не включаем;
- БД данных NewNamespace.
- Создание базы данных
- C названием NewNamespace;
- В папке mgr/NewNamespace (заметим, что переменная MGRDIR доступна нам по умолчанию).
- Запись в лог завершения создания области.
Для запуска из терминала, выполним следующие команды:
Set pVars("Namespace")="NewNamespace"
Do ##class(App.Installer).setup(.pVars)
Что выведет
2015-10-16 16:26:18 0 App.Installer: Installation starting at 2015-10-16 16:26:18, LogLevel=0
2015-10-16 16:26:18 0: Creating namespace NewNamespace
2015-10-16 16:26:19 0: End Creating namespace NewNamespace
2015-10-16 16:26:19 0 App.Installer: Installation succeeded at 2015-10-16 16:26:19
2015-10-16 16:26:19 0 %Installer: Elapsed time .605257s
2015-10-16 16:26:18 0: Creating namespace NewNamespace
2015-10-16 16:26:19 0: End Creating namespace NewNamespace
2015-10-16 16:26:19 0 App.Installer: Installation succeeded at 2015-10-16 16:26:19
2015-10-16 16:26:19 0 %Installer: Elapsed time .605257s
Список элементов
Манифест формируется из следующих элементов:
Элемент | Родительский элемент | Атрибуты (значение по умолчанию) | Описание |
Arg | Invoke, Error | Value – значение аргумена | Передаёт аргумент в метод вызываемый через Invoke или Error |
ClassMapping | Configuration | Package – пакет для отображения From – имя БД из которой отображаем |
Создаёт маппинг классов из БД в облась, внутри элемента Configuration которой мы находимся |
Compile | Namespace | Class – имя класс(ов) для компиляции Flags – флаги компиляции (ck) IgnoreErrors – игнорировать ошибки (0) |
Компилирует классы. Вызывает $System.OBJ.Compile(Class, Flags) |
Configuration | Namespace | Необходим для создания области и баз данных. Закрывающий тэг активирует маппинги и обновляет cpf файл | |
CopyClass | Namespace | Src — исходный класс Target — целевой класс Replace — удалять исходный класс (0) |
Копирует или перемещает исходный класс в целевой |
CopyDir | Manifest | Src — исходная директория Targe — целевая директория IgnoreErrors — игнорировать ошибки (0) |
Копирует директорию |
CopyFile | Manifest | Src – исходный файл Targe – целевой файл IgnoreErrors – игнорировать ошибки (0) |
Копирует файл |
Credential | Production | Name – название реквизита доступа Username – имя пользователя Password – пароль Overwrite – переопределить в случае существования |
Создаёт или переопределяет реквизиты доступа |
CSPApplication | Namespace | AuthenticationMethods – доступные методы аутентификации AutoCompile – автокомпиляция CSPZENEnabled – флаг CSP/ZEN ChangePasswordPage – страница изменения пароля CookiePath – путь для cookie сессии CustomErrorPage – пользовательская страница ошибок DefaultSuperclass – супер-класс по умолчанию DefaultTimeout – таймаут сессии Description – описание Directory – путь к CSP-файлам EventClass – класс события Grant – список ролей, которые выдаются при входе GroupById – группировать по идентификатору InboundWebServicesEnabled – входящие веб-службы IsNamespaceDefault – приложение для области по умолчанию LockCSPName – блокировка на имени CSP LoginClass – страница входа в систему PackageName – имя пакета PermittedClasses – разрешенные классы Recurse – поддиректории (0) Resource – требуемый ресурс ServeFiles – служебные файлы ServeFilesTimeout – таймаут у служебных файлов TwoFactorEnabled – двухфакторная аутентификация Url – название веб-приложения UseSessionCookie – использовать cookie для сессии |
Создаёт или изменяет веб-приложение. Подробное описание настроек в документации и классе Security.Applications |
Database | Configuration | BlockSize – размер блока ClusterMountMode – монтировать БД как часть кластера Collation – сортировка Create – создать БД — yes,no,overwrite (yes) Dir – директория Encrypted – зашифровать EncryptionKeyID – ID ключа шифрования ExpansionSize – расширение InitialSize – начальный размер MaximumSize – минимальный размер MountAtStartup – монтирование при запуске MountRequired – монтирование обязательно Name – имя PublicPermissions – общедоступные права Resource – ресурс StreamLocation – расположение потока |
Создаёт или изменяет базу данных. Подробное описание настроек в документации и классе Config.Databases |
Default | Manifest | Name – имя переменной Value – значение переменной Dir – значение переменной, если это путь к папке/файлу |
Определяет значение переменной, если она не определена |
Else | Manifest, Namespace | Выполняется, если проверка if-условия закончилась отрицательно | |
Error | Manifest | Status – код ошибки Source – источник ошибки |
Выкидывает исключение. ${} и #{} синтаксис недоступен |
ForEach | Manifest | Index – название переменной Values – список значений переменной |
Оператор совместного цикла |
GlobalMapping | Configuration | Global – имя глобала From – имя БД из которой отображаем Collation – сортировка (Cache Standard) |
Отображает глобал |
If | Manifest, Namespace | Condition – условие | Оператор условного перехода |
IfDef | Manifest, Namespace | Var – имя переменной | Оператор условного перехода, в случае если переменная определена |
IfNotDef | Manifest, Namespace | Var – имя переменной | Оператор условного перехода, в случае если переменная не определена |
Import | Namespace | File – файп/папка для импорта Flags — флаги компиляции (ck) IgnoreErrors — игнорировать ошибки (0) Recurse – рекурсивный импорт (0) |
Импортирует файлы. Вызывает: $System.OBJ.ImportDir(File,,Flags,,Recurse) и $System.OBJ.Load(File, Flags) |
Invoke | Namespace | Class – класс Method – метод CheckStatus – проверять возвращаемый статус Return – записать результат в переменную |
Вызывает метод класса, можно передавать аргументы и получить результат исполнения |
LoadPage | Namespace | Name – путь к CSP странице Dir – папка с CSP страницами Flags — флаги компиляции (ck) IgnoreErrors — игнорировать ошибки (0) |
Загружает CSP файлы через $System.CSP.LoadPage(Name, Flags) и $System.CSP.LoadPageDir(Dir, Flags) |
Log | Manifest | Level – уровень логирования от 0 (минимум) до 3 (подробно) Text – текст, до 32000 символов |
Добавляет сообщение в лог, если уровень логирования больше или равен атрибуту level |
Manifest | Корневой элемент. Единственный в манифесте, все остальные элементы располагаются внутри него | ||
Namespace | Manifest | Name – имя области Сreate – создавать область – yes,no,overwrite (yes) Code – БД с кодом Data – БД с данными Ensemble – включать Ensemble в области Прочие атрибуты связаны с веб-приложениями Ensemble |
Определяет область работы установщика |
Production | Namespace | Name – имя продукции AutoStart – автоматический запуск продукции |
Настраивает продукцию Ensemble |
Resource | Manifest | Name – имя ресурса Description – описание Permission – общедоступные права |
Создаёт или изменяет ресурс |
Role | Manifest | Name – имя роли Description – описание Resources – ресурсы в виде «MyResource:RW,MyResource1:RWU» RolesGranted – дать сопутствующие роли |
Создаёт роль |
RoutineMapping | Configuration | Routines – имя рутин(ы) Type – один из типов «MAC,INT,INC,OBJ,ALL» From – из какой БД |
Создаёт маппинг рутин |
Setting | Production | Item – настраиваемый элемент Target – тип настройки: Item, Host, Adapter Setting – название настройки Value – значение настройки |
Конфигурирует элемент продукции Ensemble. Вызывает метод Ens.Production:ApplySettings |
SystemSetting | Manifest | Name – класс.свойство пакета Config Value – значение свойства |
Устанавливает значение свойств пакета Config (через метод Modify) |
User | Manifest | Username – имя пользователя PasswordVar – переменная, содержащая пароль Roles – список ролей пользователя Fullname – полное имя Namespace – стартовая область Routine – стартовая рутина ExpirationDate – дата, после которой пользователь перестанет быть активированным ChangePassword – сменить пароль при следующем логине Enabled – активирован ли пользователь |
Создаёт или изменяет пользователя |
Var | Manifest | Name — имя переменной Value – значение переменной |
Определяет значение переменной |
Переменные
В качестве значений атрибутов могут выступать переменные, они определяются одним из трёх способов:
- ${<Имя_переменной>} – во время выполнения манифеста вычисляется значение переменной (переданное пользователем или одна из переменных среды, см. далее);
- ${#<Имя_параметра>} – во время компиляции заменяются на значение указанного параметра класса установщика;
- #{<COS_код>} — во время выполнения манифеста вычисляется значение указанного COS выражения. Не забывайте о кавычках.
Значения параметров определяются на этапе компиляции, поэтому они могут быть частью переменой или COS выражения. Интерпретация переменных происходит перед интерпретацией COS-кода, поэтому переменные могут входить в COS-выражение, например:
#{$ZCVT("${NAMESPACE}","L")}.
Переменные среды
Всегда доступны следующие переменные:
Переменная | Описание | Пример значения |
SourceDir | (Только при установке Cache) Директория, где находится установщик (setup_cache.exe or cinstall) | /InterSystems/distr/ |
ISCUpgrade | (Только при установке Cache) Определяет, установка или обновление | 0 (установка) 1 (обновление) |
CFGDIR | См. INSTALLDIR. | /InterSystems/Cache/ |
CFGFILE | Путь к cpf файлу | /InterSystems/Cache/cache.cpf |
CFGNAME | Имя инстанса | CACHE |
CPUCOUNT | Количество ядер CPU | 4 |
CSPDIR | Директория CSP | /InterSystems/Cache/csp/ |
HOSTNAME | Имя веб сервера | SCHOOL15 |
HTTPPORT | Порт веб сервера | 80 |
INSTALLDIR | Директория, куда устанавливается Cache | /InterSystems/Cache/ |
MGRDIR | Директория менеджмента (mgr) | /InterSystems/Cache/mgr/ |
PLATFORM | Операционная система | UNIX |
PORT | Порт суперсервера Cache | 1972 |
PROCESSOR | Имя платформы | x86-64 |
VERSION | Версия Cache | 2015.1.1 |
Отладка
Бывает непонятно, какие значения могут принимать атрибуты. Для выяснения посмотрите на сгенерированный int-код метода setup. Как правило, основной вызов это метод:
tInstaller.<ElementName>
, т.е. метод класса %Installer.Installer который уже вызывает непосредственно системные методы. Альтернативно, можно посмотреть на код класса %Installer.<ElementName>
, атрибуты элемента это свойства его класса. Кодогенерация осуществляется в методах %OnBeforeGenerateCode, %OnGenerateCode, %OnAfterGenerateCode.Для целей отладки, рекомендую оборачивать вызов установщика в транзакцию. Так с помощью команд TSTART/TROLLBACK можно легко отменить все изменения, сделанные внутри Cache (внешние к Cache изменения, например создание файла БД отменены не будут).
Пример использования
Проект MDX2JSON содержит установщик. Для установки достаточно импортировать в любую область файл installer.xml с классом MDX2JSON.Installer. Импортировать можно или через панель управления или просто переносом класса в студию.
Далее выполните в терминале:
do ##class(MDX2JSON.Installer).setup()
В результате произойдёт загрузка файлов из GitHub-репозитория, установка «по умолчанию» в область MDX2JSON с созданием базы MDX2JSON, маппинга пакета MDX2SJON в %All, создание REST-приложения /MDX2JSON и многое другое, о чём будет написано в терминале.
Ещё примеры
Пример в документации.
Класс Sample.Installer в области Samples.
Проект CacheGitHubCI содержит установщик.
Проект SYSMON Dashboards содержит установщик.
Проект DeepSee Audit содержит установщик.
Выводы
Утилиту %Installer можно использовать для поставки решений на InterSystems Cache и Ensemble.
Ссылки
Документация
morisson
Есть еще альтернативный вариант поставки — как в этом проекте. Все компоненты (и Cache и web app) собираются gulp в один файл xml, а при импортировании xml в Cache происходит сборка классов и развертывание веб-приложения. Т.е. для установки и настройки всех компонентов приложения достаточно, например, кинуть xml в окно студии. Например как показано здесь.