kondate
                                
                                
                                
                                    kondate copied to clipboard
                            
                            
                            
                        Kondate is yet another nodes management framework for Itamae/Serverspec
Kondate
Kondate is yet another nodes management framework for Itamae/Serverspec.
Kondate provides nodes/roles/attributes/run_lists management feature for Itamae/Serverspec.
Installation
Add this line to your application's Gemfile:
gem 'kondate'
And then execute:
$ bundle
Or install it yourself as:
$ gem install kondate
Usage
Generate a template directory tree:
$ bundle exec kondate init .
Run itamae:
$ bundle exec kondate itamae <host>
Run serverspec:
$ bundle exec kondate serverspec <host>
Run itamae for multiple hosts of a given role in parallel:
$ bundle exec kondate itamae-role <role>
Run serverspec for multiple hosts of a given role in parallel:
$ bundle exec kondate serverspec-role <role>
Configuration
kondate init provides a template directory tree such as:
.
├── .kondate.conf     # kondate configuration
├── bootstrap.rb      # itamae bootstrap
├── hosts.yml         # manages hostnames and its roles
├── properties        # manages run_lists and attributes
│   ├── nodes         # host specific properties
│   ├── roles         # role properties
│   └── environments  # environment properties
├── recipes           # itamae recipes
│   ├── middleware    # middleware recipes
│   │   └── base
│   │       └── default.rb
│   └── roles         # role recipes
└── spec              # serverspec specs
    ├── middleware    # middleware recipes specs
    │   └── base_spec.rb
    ├─  roles         # role recipes specs
    └── spec_helper.rb
├── secrets           # manages secrets attributes such as passwords
│   ├── properties
│   │   ├── environments
│   │   ├── nodes
│   │   └── roles
│   ├── recipes
│   │   ├── middleware
│   │   └── roles
│   └── spec
│       ├── middleware
│       └── roles
.kondate.conf
The default .kondate.conf looks like below:
middlware_recipes_dir: recipes/middleware
roles_recipes_dir: recipes/roles
middleware_recipes_serverspec_dir: spec/middleware
roles_recipes_serverspec_dir: spec/roles
nodes_properties_dir: properties/nodes
roles_properties_dir: properties/roles
environments_properties_dir: properties/environments
secret_middlware_recipes_dir: secrets/recipes/middleware
secret_roles_recipes_dir: secrets/recipes/roles
secret_middleware_recipes_serverspec_dir: secrets/spec/middleware
secret_roles_recipes_serverspec_dir: secrets/spec/roles
secret_nodes_properties_dir: secrets/properties/nodes
secret_roles_properties_dir: secrets/properties/roles
secret_environments_properties_dir: secrets/properties/environments
plugin_dir: lib
host_plugin:
  type: file
  path: hosts.yml
You can customize the directory tree with this conf.
Itamae options
You can configure itamae-kondate options on .kondate.conf, too as:
itamae_options:
  shell: /bin/bash
See itamae-kondate --help for option list.
Serverspec options
You can configure serversepc-kondate options on .kondate.conf, too as:
serverspec_options:
  vagrant: true
See serverspec-kondate --help for option list.
hosts.yml
The default uses file host plugin, and hosts.yml. The contents of hosts.yml look like below:
localhost: [sample]
where keys are host names, and values are array of roles.
$ bundle exec kondate itamae <host>
works as follows:
- obtains a role list from 
hosts.yml - reads 
properties/roles/#{role}.yml, and find recipes and its attributes - runs recipes
 
You can create your own host plugin. See Host Plugin section for more details.
properties
Property files are places to write recipes to run and attributes values.
├── properties        # manages run_lists and attributes
│   ├── nodes         # host specific properties
│   ├── roles         # role properties
│   └── environments  # environment properties
An example looks like below:
properties/roles/#{role}.yml
attributes:
  ruby:
    versions: [2.2.3]
    gems:
      2.2.3: [bundler]
  node:
    versions: [v0.12.2]
    global: v0.12.2
  nginx:
The attributes variables are accessible like attrs['ruby']['versions'], which is equivalent and short version of node['attributes']['ruby']['versions'] in recipes.
You can also prepare host-specific property files such as:
properties/nodes/#{host}.yml
attributes:
  nginx:
    worker_processes: 8
In addition, you can also prepare environment property files such as:
properties/environments/#{ENV['ENVIRONMENT'] || 'development'}.yml
global_attributes:
  aws_region: ap-northeast-1
where global_attributes is accessible like global_attrs['aws_region'], which is equivalent and short version of node['global_attributes']['aws_region'] in recipes.
These files are merged on kondate execution in order of environment + role + node (node > role > environment in the strong order).
secret properties
Secret properties are places to write confidential attributes.
├── secrets         # manages secrets attributes such as passwords
│   └── properties
│       ├── nodes
│       ├── roles
│       └── environments
An example looks like below:
secrets/properties/roles/sample.yml
attributes:
  base:
    password: xxxxxxxx
These files are merged with property files on kondate execution.
Hint: I manage secret property files on github private repository. ToDo: support encryption.
recipes
Put you itamae recipes:
├── recipes         # itamae recipes
│   ├── middleware  # middleware recipes
│   │   └── base
│   │       └── default.rb
│   └── roles       # role recipes
middleware recipes are usual recipes to write how to install middleware such as nginx, mysql.
role recipes are places to write role-specific provisioning. I often write recipes to create log directories for my app (role), for example.
recipes/roles/myapp/default.rb
directory "/var/log/myapp" do
  owner myapp
  group myapp
  mode 0755
end
spec
Put your serverspec specs.
└── spec            # serverspec specs
    ├── middleware  # middleware recipes specs
    └── roles       # role recipes specs
It is required that spec/spec_helper has lines:
set :host, ENV['TARGET_HOST']
set :set_property, YAML.load_file(ENV['TARGET_NODE_FILE'])
because these ENVs are passed by kondate serverspec.
Configuring following lines for vagrant is also recommended:
  if ENV['TARGET_VAGRANT']
    `vagrant up #{host}`
    config = Tempfile.new('', Dir.tmpdir)
    config.write(`vagrant ssh-config #{host}`)
    config.close
    Net::SSH::Config.for(host, [config.path])
  else
ENV['TARGET_VAGRANT'] is turned on if kondate serverspec is executed with --vagrant option.
See templates/spec/spec_helper.rb for an example.
Exploring role files
Available version: >= v0.4.0
Assume role is delimited with - (you can configure the delimiter) such as myapp-web-staging, this feature explores role files in order of:
- myapp-web-staging.yml
 - myapp-web-base.yml
 - myapp-web.yml
 - myapp-base.yml
 - myapp.yml
 - base.yml
 
This makes it possible to share a property file, for example, myapp-web.yml among myapp-web-staging and myapp-web-production roles.
To enable this feature, you need to configure .kondate.conf as:
explore_role_files: true # default is false
role_delimiter: "-" # default is -
Host Plugin
The default reads hosts.yml to resolve roles of a host, but
you may want to resolve roles from AWS EC2 roles tag, or
you may want to resolve roles from your own host resolver API application.
Thus, kondate provides a plugin system to resolve hosts' roles.
Naming Convention
You must follow the below naming conventions:
- gem name: kondate-host_plugin-xxx (xxx_yyy) (if you want to make a gem)
 - file name: lib/kondate/host_plugin/xxx.rb (xxx_yyy.rb)
 - class name: Kondate::HostPlugin::Xxx (XxxYyy)
 
If you want to put your own host plugin locally without publishing a gem, you can configure the location with .kondate.conf as:
plugin_dir: lib
Interface
What you have to implement are #initialize, #get_environment, and #get_roles methods.
get_hostinfo method is an optional method to return arbitrary hostinfo of the host (available from kondate 0.2.0).
Here is an example of file plugin:
require 'yaml'
module Kondate
  module HostPlugin
    # YAML format
    #
    # host1: [role1, role2]
    # host2: [role1, role2]
    class File < Base
      # @param [HashWithIndifferentAccess] config
      def initialize(config)
        super
        raise ConfigError.new('file: path is not configured') unless config.path
        @path = config.path
        @roles_of_host = YAML.load_file(@path)
        @hosts_of_role = {}
        @roles_of_host.each do |host, roles|
          roles.each do |role|
            @hosts_of_role[role] ||= []
            @hosts_of_role[role] << host
          end
        end
      end
      # @param [String] host hostname
      # @return [String] environment name
      def get_environment(host)
        ENV['ENVIRONMENT'] || 'development'
      end
      # @param [String] host hostname
      # @return [Array] array of roles
      def get_roles(host)
        @roles_of_host[host]
      end
      # @param [String] role role
      # @return [Array] array of hosts
      #
      # Available from kondate >= 0.3.0
      def get_hosts(role)
        @hosts_of_role[role]
      end
      # Optional
      #
      # @param [String] host hostname
      # @return [Hash] arbitrary hostinfo
      def get_hostinfo(host)
        {}
      end
    end
  end
end
Config
config parameter of #initialize is created from the configuration file (.kondate.conf):
host_plugin:
  type: file
  path: hosts.yml
config.type and config.path is available in the above config.
See Also
Development
bundle exec exe/kondate init .
vagrant up
bundle exec exe/kondate itamae vagrant-centos --vagrant --role sample
bundle exec exe/kondate serverspec vagrant-centos --vagrant --role sample
ToDo
write tests
License
The gem is available as open source under the terms of the MIT License.