Привет.

Некоторое время назад я обзывал rudeck плохими словами и некоторые из них, в частности обвинение в глючности, я готов взять обратно. На днях поставил его самостоятельно, последнюю версию (1.6.2) и он зафурычил с полпинка.

Так вот, сегодняшняя заметка будет посвещена rundeck, ridley и совсем немного Jenkins.

Итак, допустим мы установили rundeck ( можно скачать пакет тут), запустили и хотим как-то впилить список нод. Желательно из Chef. Желательно с параметрами. И его можно закинуть на сервер в формате YAML (Когда наконец в opscode поймут что JSON — говно?!) или XML есть несколько способов — файл, каталог с файлами, скрипт и веб-страница. И для шефа уже существует плагин chef-rundeck, который запускат локальный веб-сервер и генерирует страничку, которую мы скармливаем в rundeck. Но у этого способа есть фатальный недостаток, к тому же в список не кастомизируется и я считаю очень глупым имея базу данных в chef не использовать ее в rundeck. Поэтому я решил немного повозиться и использовать одну прикольную либу от создателей Berkshelf.

Ridley

Ridley — библиотека для работы с chef-сервером. Она удобна, быстра и дьявольски коварна, но лучше чем ничего. Естественно, она написана на ruby и для ruby. README файл на гитхабе описывает почти все, а о том, что не описывает и что я заметил — напишу сегодня. Для удобства парсинга аргументов я заюзал Trollop ( wiki ) и надеюсь что все будет достаточно понятно. В итоге нам надо получить файл вида:

hostname: "ec2-10-00-00-01.compute-1.amazonaws.com"
  type: "Node"
  description: "node1.example.com"
  tags: 
    - base_server
      redis_server
      prod
  username: "rundeck-ssh"
  env: "prod"

Итак, создадим скрипт.

#!/usr/bin/env ruby
require "ridley"
require "trollop"
require 'uri'

opts = Trollop::options do
  version "rchef-list 0.0.1 (c) 2013 Grammarly"
  banner <<-EOS
Test is an awesome program that does simple list of chef nodes for rundeck server.

Usage:
       rchef-list [options]
where [options] are:
EOS
  opt :server_url, "Enter chef server url. Example: https://api.opscode.com/organizations/ridley", :short => "s", :type => :string
  opt :client_name, "Chef client name", :short => "c", :type => :string
  opt :client_key, "Client key", :short => "k", :type => :string
end

Trollop::die "server_url are required" unless opts[:server_url]
Trollop::die "client_key are required" unless opts[:client_key]
Trollop::die "client_name are required" unless opts[:client_name]
Trollop::die :server_url, "must be vaid url" unless opts[:server_url] =~ URI::regexp
Trollop::die :client_key, "must exist" unless File.exist?(opts[:client_key]) if opts[:file]

И заставим его коннектиться к серверу:

ridley = Ridley::new(
  server_url: opts[:server_url],
  client_name: opts[:client_name],
  client_key: opts[:client_key]
)

После чего мы получим вполне себе обьект, через которым мы сможем этим сервером управлять.

По-умолчанию, создатели рекомендуют нам для получения списка нод использовать ridley.node.all, но то ли это моя повышенная криворукость, то ли стремление создателей к высокой скорости, но обьекты которые я получил содержали в себе из полезной информации лишь имена нод. Это плохо, но расстраиваться не надо — есть другой способ. nodes = ridley.search(:node) — он немного дольше, но результат устраивает и, получив обьект nodes, мы продолжим чудесное плаванье:

hash = Hash.new
nodes.each do |node|
  if node.name
    hash[node.name] = {}
    hash[node.name]["hostname"] = node.automatic.ec2 ?node.automatic.ec2.public_hostname : node.name
    hash[node.name]["type"] = "Node"
    hash[node.name]["description"] = node.name
    hash[node.name]["tags"] = "#{node.automatic.roles.join(",")},#{node.chef_environment}" unless node.automatic.roles.nil?
    hash[node.name]["username"] = "rundeck-ssh"
    hash[node.name]["env"] = node.chef_environment
  end
end
puts hash.to_yaml

Самые умные уже успели догадаться что я создал хеш и наполнил его датой. Сделал я это для того, чтобы на выходе переобразовать все в YAML, так как именно его ест rundeck. Хочу обратить ваше внимание на необходимые поля — имя ноды (оглавление хеша), hostname — адрес сервера и username — имя юзера под которым вы будете к серверу подключаться. Все остальное — необязательные переменные, которые, впрочем, помогут вам взаимодествовать с сервером rundeck.

Стандартно rundeck парсит только nodename, hostname, username, description, tags, osFamily, osArch, osName, osVersion, editUrl, remoteUrl. И это оставляет меня в глубоком недоумении — о чем думали создатели, когда делали такой список? Почему нельзя парсить по самосозданным полям? Непонятно… И я очень надеюсь, что в версии 2.0, которая была анонсирована недавно, они это исправят.

Итак, воздуха для творчества немного, поэтому я запихнул все нужные мне поля — роли ноды и окружение — в теги.

Теперь второй прикол — теги в rundeck по-умолчанию перечисляются через запятую и не уточняют друг друга, а дополняют. И мне понадобилось минут тридцать, чтобы допереть использовать «+», так как в документации нигде явно это не написано. В остальном rundeck достаточно прост.

И еще, как и обещал — есть некий плагин рандека для jenkins. Хочу отметить что он крутой и позволяет вытягивать список билдов и артифактов в опции rundeck. Настоятельно рекомендую ознакомиться.

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