Клиенты командной строки - это широко распространенный стандарт, сопровождающий restful API и сервисы. Они позволяют разработчикам легко использовать функциональные возможности конкретных служб в сценариях автоматизации, непрерывной интеграции или служебных функциях личной оболочки. NodeJS - отличный язык-кандидат для создания сценариев для клиентов командной строки благодаря активному сообществу, предоставляющему различные модули для использования в качестве строительных блоков для вашего интерфейса командной строки.

Один из этих модулей сообщества, yargs, помогает создавать интерактивные инструменты командной строки, анализируя аргументы и предоставляя элегантный интерфейс компоновщика для генерации команд и подкоманд в коде. Новую команду и аргумент можно зарегистрировать с помощью обратного вызова в несколько простых строк кода.

// $> ./bin/cli.js sample --argument1 myfirstargument
yargs.command({
    name: 'sample',
    description: 'sample command',
    options: {
        argument1: {
            required: true,
            description: 'First argument',
            alias: 'a'
        }
    }
}, callback: (argv) => {
    console.info(`sample command with arg: ${argv.argument1}`);
});

В Containership нашей целью было создание интерфейса командной строки с легко расширяемыми компонентами (которые мы называем плагинами). Для этого мы адаптировали формат yargs.command в соответствии с нашими потребностями в рекурсивной, но все еще простой структуре. Сторонние плагины необходимы для экспорта ключа cli из модуля узла в следующем формате:

module.exports.cli = {
    name: 'sample-plugin-command',
    description: 'Sample command',
    commands: []
    // options: { ... }
    // callback: (argv) => { ... }
};

Есть пять возможных ключей для использования в определении команды:

name - собственно имя подкоманды

description - собственно имя подкоманды

options - объект опции yargs (подробнее см. Http://yargs.js.org/docs/#api-optionskey-opt)

callback - обратный вызов зарегистрирован с командой; он вызывается с argv, который включает все проанализированные параметры командной строки для команды

commands - массив подкоманд (определение аналогично команде верхнего уровня с именем, описанием, параметрами, обратным вызовом и клавишами команд)

Эти пять клавиш позволяют легко создавать мощные и сложные подкоманды. В качестве примера из плагина Containership Cloud мы создаем подкоманду для входа в учетную запись, которая требует обязательного параметра токена.

$> csctl cloud account login --token *********

module.exports = {
    name: 'cloud',
    description: 'Containership Cloud Plugin',
    commands: []
}
module.exports.commands.push({
    name: 'account',
    description: 'Show and manipulate cloud account.',
    commands: [
        {
            name: 'login',
            description: 'Login command for cloud services.',
            options: {
              token: {
                required: true
              }
            },
            callback: (argv) => { ... }
        }
    ]
});

Команда будет автоматически проанализирована yargs, а затем будет запущен обратный вызов нашей подкоманды под login с полученными аргументами. Вы можете получить доступ к параметру token, просто обратившись к argv.token. Это позволяет нашим конечным пользователям легко расширять базовый интерфейс командной строки с помощью собственных функций любым способом, который они сочтут нужным!

Наш загрузчик плагинов поддерживает конечный набор вложенных подкоманд, чтобы ограничить конечных пользователей от создания вложенных команд после определенного порогового значения, но вы можете легко разрешить вложенные команды до любой глубины, которая вам нужна.

Чтобы проанализировать эти команды подключаемых модулей, база CLI ищет подключаемые модули, хранящиеся в каталоге конфигурации, и пытается рекурсивно загрузить команды, которые они экспортируют. Функция parseCommand сначала проверяет, включает ли текущее определение массив commands, и если да, пытается рекурсивно загрузить подкоманды, используя функциональность yargs.builder. Каждая последующая команда запускает рекурсивный parseCommand вызов с внутренним определением команды. Базовый случай рекурсивной функции - если она находит ключ callback, и в этом случае она использует синтаксис yargs.command для регистрации команды.

parseCommand(yargs, def, depth = 0) {
  if(++depth > MAX_SUBCOMMAND_DEPTH) {
    throw new Error('Max depth exceeded!');
  }
  // has sub-commands defined
  if(def.commands) {
    yargs.command({
      command: def.name,
      description: def.description,
      builder: (yargs) => {
        let hasDefault = false;
        _.forEach(def.commands, (cmd) => {
          hasDefault = hasDefault || cmd === '*';
          return this.parseCommand(yargs, cmd, depth);
        });
        if(!hasDefault) {
          yargs.command('*', '', {}, () => {
            _.forEach(
              _.sortBy(def.commands, cmd => cmd.name), cmd => {
                hasDefault = hasDefault || cmd === '*';
                return this.parseCommand(yargs, cmd, depth);
              }
            );
            return yargs.showHelp('log');
          });
        }
      }
    });
  } else if(def.callback) {
    yargs.command(
      def.name, 
      def.description, 
      def.options || {}, 
      def.callback
    );
  }
}

Функция синтаксического анализа также предоставляет help информацию и флаги по умолчанию для каждой из команд. Самое приятное то, что все, что поддерживает синтаксический анализатор параметров модуля yargs, наш синтаксический анализатор также поддерживает его, поскольку мы просто проксируем через модуль. Это дает вам массу возможностей для проверки параметров и других функций прямо из коробки.

Посетите https://github.com/containership/containership.cli, чтобы увидеть наш базовый клиент, который обрабатывает функции по умолчанию, а также анализирует расширения плагинов. Чтобы увидеть полноценный плагин CLI в действии, ознакомьтесь с https://github.com/containership/containership.plugin.cloud/blob/master/lib/cli-v2.js, чтобы узнать, как мы используем встроенный соглашение, позволяющее легко расширять и создавать новые команды.

Containership - это платформа для простой подготовки и управления кластером контейнеров в мультиоблачной среде. Автоматизация никогда не была такой простой, благодаря нашему интерфейсу командной строки и простоте расширяемости, созданной на его основе. Посетите https://www.containership.io для получения дополнительной информации или обратитесь к [email protected], если у вас есть какие-либо вопросы.