Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Major refactor of #chef_role and #chef_search and ChefZero tests #28

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .chef/knife.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
current_dir = File.dirname(__FILE__)

node_name "capistrano-chef-test"
client_key "#{current_dir}/stickywicket.pem"
chef_server_url "http://127.0.0.1:8889"
27 changes: 27 additions & 0 deletions .chef/stickywicket.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA0sOY9tHvVtLZ6xmVmH8d8LrRrNcWOXbrvvCrai+T3GtRvRSL
hksLrpOpD0L9EHM6NdThNF/eGA9Oq+UKAe6yXR0hwsKuxKXqQ8SEmlhZZ9GiuggD
B/zYD3ItB6SGpdkRe7kQqTChQyrIXqbRkJqxoTXLyeJDF0sCyTdp3L8IZCUWodM8
oV9TlQBJHYtG1gLUwIi8kcMVEoCn2Q8ltCj0/ftnwhTtwO52RkWA0uYOLGVayHsL
SCFfx+ACWPU/oWCwW5/KBqb3veTv0aEg/nh0QsFzRLoTx6SRFI5dT2Nf8iiJe4WC
UG8WKEB2G8QPnxsxfOPYDBdTJ4CXEi2e+z41VQIDAQABAoIBAALhqbW2KQ+G0nPk
ZacwFbi01SkHx8YBWjfCEpXhEKRy0ytCnKW5YO+CFU2gHNWcva7+uhV9OgwaKXkw
KHLeUJH1VADVqI4Htqw2g5mYm6BPvWnNsjzpuAp+BR+VoEGkNhj67r9hatMAQr0I
itTvSH5rvd2EumYXIHKfz1K1SegUk1u1EL1RcMzRmZe4gDb6eNBs9Sg4im4ybTG6
pPIytA8vBQVWhjuAR2Tm+wZHiy0Az6Vu7c2mS07FSX6FO4E8SxWf8idaK9ijMGSq
FvIS04mrY6XCPUPUC4qm1qNnhDPpOr7CpI2OO98SqGanStS5NFlSFXeXPpM280/u
fZUA0AECgYEA+x7QUnffDrt7LK2cX6wbvn4mRnFxet7bJjrfWIHf+Rm0URikaNma
h0/wNKpKBwIH+eHK/LslgzcplrqPytGGHLOG97Gyo5tGAzyLHUWBmsNkRksY2sPL
uHq6pYWJNkqhnWGnIbmqCr0EWih82x/y4qxbJYpYqXMrit0wVf7yAgkCgYEA1twI
gFaXqesetTPoEHSQSgC8S4D5/NkdriUXCYb06REcvo9IpFMuiOkVUYNN5d3MDNTP
IdBicfmvfNELvBtXDomEUD8ls1UuoTIXRNGZ0VsZXu7OErXCK0JKNNyqRmOwcvYL
JRqLfnlei5Ndo1lu286yL74c5rdTLs/nI2p4e+0CgYB079ZmcLeILrmfBoFI8+Y/
gJLmPrFvXBOE6+lRV7kqUFPtZ6I3yQzyccETZTDvrnx0WjaiFavUPH27WMjY01S2
TMtO0Iq1MPsbSrglO1as8MvjB9ldFcvp7gy4Q0Sv6XT0yqJ/S+vo8Df0m+H4UBpU
f5o6EwBSd/UQxwtZIE0lsQKBgQCswfjX8Eg8KL/lJNpIOOE3j4XXE9ptksmJl2sB
jxDnQYoiMqVO808saHVquC/vTrpd6tKtNpehWwjeTFuqITWLi8jmmQ+gNTKsC9Gn
1Pxf2Gb67PqnEpwQGln+TRtgQ5HBrdHiQIi+5am+gnw89pDrjjO5rZwhanAo6KPJ
1zcPNQKBgQDxFu8v4frDmRNCVaZS4f1B6wTrcMrnibIDlnzrK9GG6Hz1U7dDv8s8
Nf4UmeMzDXjlPWZVOvS5+9HKJPdPj7/onv8B2m18+lcgTTDJBkza7R1mjL1Cje/Z
KcVGsryKN6cjE7yCDasnA7R2rVBV/7NWeJV77bmzT5O//rW4yIfUIg==
-----END RSA PRIVATE KEY-----
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
*.gem
.bundle
.chef
Gemfile.lock
pkg/*
4 changes: 4 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
source 'https://rubygems.org'

# Specify your gem's dependencies in capistrano-chef.gemspec
gemspec
15 changes: 8 additions & 7 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
require 'bundler'
require 'rspec/core/rake_task'
Bundler::GemHelper.install_tasks
require "bundler/gem_tasks"
require "rake/testtask"

desc 'Default: run specs.'
task :default => :spec
Rake::TestTask.new(:test) do |t|
t.libs << "test"
t.test_files = FileList['test/**/*test.rb']
t.verbose = !!ENV['DEBUG']
end

desc 'Run specs'
RSpec::Core::RakeTask.new
task :default => :test
22 changes: 14 additions & 8 deletions capistrano-chef.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,26 @@ require "capistrano/chef/version"

Gem::Specification.new do |s|
s.name = "capistrano-chef"
s.version = CapistranoChef::VERSION.dup
s.version = Capistrano::Chef::VERSION
s.platform = Gem::Platform::RUBY
s.license = 'MIT'
s.authors = ['Ryan Oblak']
s.email = ['[email protected]']
s.homepage = "https://github.com/rroblak/capistrano-chef"
s.summary = %q{Capistrano 3 extensions for Chef integration}
s.description = %q{Allows Capistrano to use Chef data for deployment}
s.authors = ['Ryan Oblak', 'Justin Reagor']
s.email = ['[email protected]', '[email protected]']
s.homepage = "https://github.com/gofullstack/capistrano-chef"
s.summary = %q{Capistrano 3 extensions for working with Chef}
s.description = %q{Allows Capistrano and Chef to work together to script release tasks}

s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
s.add_dependency 'capistrano', '>= 3'
s.add_dependency 'chef', '>= 0.10.10'

s.add_dependency "capistrano", "~> 3.2.1"
s.add_dependency "chef", "~> 11.12.8"

s.add_development_dependency "bundler", "~> 1.6"
s.add_development_dependency "rake"
s.add_development_dependency "minitest"
s.add_development_dependency "chef-zero"
end

Empty file removed capistrano-chef.rb
Empty file.
37 changes: 0 additions & 37 deletions chef.rb

This file was deleted.

1 change: 0 additions & 1 deletion lib/capistrano/chef.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,3 @@
knife.configure_chef

self.extend Capistrano::DSL::Chef

75 changes: 75 additions & 0 deletions lib/capistrano/chef/test_case.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
require "sshkit"
require "capistrano/configuration"
require "chef_zero/server"

module Capistrano
module Chef
module TestHelpers

include Capistrano::DSL::Chef

extend Forwardable

def_delegators ::Capistrano::Configuration, :env, :reset!

def setup
chef_server!
super
end

def teardown
reset!
chef_server!
super
end

def chef_server!
if server.running?
server.stop
else
server.start_background
end
end

def server
@server ||= ::ChefZero::Server.new \
port: 8889,
debug: !!ENV['DEBUG'],
single_org: false
end

def servers
env.send(:servers)
end

def servers_with_role(role)
servers.map do |server|
next unless server.properties.roles.include?(role)
yield server if block_given?
server
end
end

def nodes
@nodes ||= {}
end

def stub_node(name, &block)
name = name.to_s || "test_node_#{nodes.keys.size}"
nodes[name] = ::Chef::Node.build(name).tap(&block)
data = ::JSON.fast_generate(nodes[name])
server.load_data({ "nodes" => { name => data }})
nodes[name]
end

def method_missing(name, *args)
if env.respond_to?(name)
env.__send__(name, *args)
else
super name, *args
end
end

end
end
end
6 changes: 4 additions & 2 deletions lib/capistrano/chef/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module CapistranoChef
VERSION = '1.0.0'.freeze
module Capistrano
module Chef
VERSION = '2.0.0'
end
end
85 changes: 63 additions & 22 deletions lib/capistrano/dsl/chef.rb
Original file line number Diff line number Diff line change
@@ -1,35 +1,76 @@
module Capistrano
module DSL
module Chef
def chef_role(name, query = '*:*', options)
arg = options.delete(:attribute) || :ipaddress
#
# When module is included, provide a way to override the Chef query class
# dependency.
#
def self.included(klass)
klass.send :attr_writer, :chef_query_class
end

search_proc = case arg
when Proc
arg
when Hash
iface, family = arg.keys.first.to_s, arg.values.first.to_s
Proc.new do |n|
addresses = n["network"]["interfaces"][iface]["addresses"]
addresses.select{|address, data| data["family"] == family }.to_a.first.first
end
when Symbol, String
Proc.new{|n| n[arg.to_s]}
else
raise ArgumentError, 'Search arguments must be Proc, Hash, Symbol, String.'
end
#
# Set a Capistrano roles by searching a Chef Server for appropriate node
# data
#
# @param name [String, Symbol, Array<String, Symbol>] roles to set
# @param query [String] query for searching a chef server
# @param options [Hash] optional role and search criteria
# @param block [Proc] block used to filter search result nodes
# @return [Array<Hash>] map of hashes of user and host pairs by role name
#
def chef_role(name, query='*:*', options={}, &block)
attribute = options.delete(:attribute) || :ipaddress
results_proc = block_given? ? block : results_by(attribute)
user = fetch(:user)
hosts = chef_search(query).flat_map(&results_proc)

hosts = chef_search(query).map(&search_proc)
Array(name).flat_map do |name|
hosts.map do |host|
user = [user, host].compact.join("@")
role name, user, options
next({ name => [user, host] })
end
end
end

name = [name] unless name.is_a?(Array)
#
# Query a Chef Server to search for specific nodes
#
# @param [String] query string
# @return [Array<Chef::Node>] list of node results found
#
def chef_search(query)
chef_query_class.new.search(:node, query).first
end

user = fetch(:user)
private

name.each { |n| role(name, hosts.map { |h| "#{user ? "#{user}@" : ''}#{h}" }) }
#
# Interface dependency using Chef for searching by default
#
def chef_query_class
@chef_query_class ||= ::Chef::Search::Query
end

def chef_search(query)
Module.const_get(:Chef)::Search::Query.new.search(:node, query)[0].compact
#
# Query a Chef Server to search for specific nodes
#
def results_by(attribute)
case attribute
when Symbol, String
lambda { |node| node[attribute] }
when Hash # not tested
iface, family = attribute.keys.first.to_s, attribute.values.first.to_s
lambda do |nodes|
addresses = node["network"]["interfaces"][iface]["addresses"]
addresses.select do |address, data|
data["family"] == family
end.to_a.first.first
end
else
Proc.new {} # noop
end
end
end
end
Expand Down
15 changes: 15 additions & 0 deletions test/capistrano/chef_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require 'minitest_helper'
require 'capistrano/chef'

# HACK until this DSL is scoped within a task
$extended_modules = (class << self; self end).included_modules

class Capistrano::ChefTest < Minitest::Test
def test_that_it_has_a_version_number
refute_nil ::Capistrano::Chef::VERSION
end

def test_it_does_something_useful
assert_includes $extended_modules, ::Capistrano::DSL::Chef
end
end
46 changes: 46 additions & 0 deletions test/capistrano/dsl/chef_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
require 'minitest_helper'

class Capistrano::DSL::ChefTest < Minitest::Test

include Capistrano::Chef::TestHelpers

def test_chef_role
user = "jimbo"
hostname = "10.1.2.3"

set :user, user
stub_node :test_node_1 do |node|
node.normal.ipaddress = hostname
end

chef_role :cachey, "name:test_node_1"

servers_with_role :cachey do |server|
assert_equal user, server.user
assert_equal hostname, server.hostname
end
end

def test_chef_role_block
user = "duder"
hostname = "129.1.2.3"

set :user, user
stub_node :test_node_2 do |node|
node.normal.ipaddress = "88.1.2.3"
node.normal.network.interfaces.eth0.addresses[hostname].family = "inet"
end

chef_role :webbie, "name:test_node_2" do |node|
node["network"]["interfaces"]["eth0"]["addresses"].map do |ipaddress, address|
next ipaddress if address.family == "inet"
end
end

servers_with_role :webbie do |server|
assert_equal user, server.user
assert_equal hostname, server.hostname
end
end

end
5 changes: 5 additions & 0 deletions test/minitest_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
require 'minitest/autorun'
require 'chef_zero/server'
require 'capistrano/chef'
require 'capistrano/chef/test_case'