capistrano-chef 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - ree
4
+ - 1.9.2
5
+ - 1.9.3
data/Gemfile CHANGED
@@ -1,4 +1,10 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- # Specify your gem's dependencies in capistrano-chef.gemspec
3
+ group :test do
4
+ gem 'rake'
5
+ gem 'rspec'
6
+ gem 'capistrano-spec'
7
+ gem 'guard-rspec'
8
+ end
9
+
4
10
  gemspec
data/Guardfile ADDED
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
data/README.md CHANGED
@@ -1,41 +1,97 @@
1
- capistrano-chef
2
- ===============
1
+ # Capistrano Chef [![Build Status](https://secure.travis-ci.org/cramerdev/capistrano-chef.png?branch=master)](http://travis-ci.org/cramerdev/capistrano-chef)
3
2
 
4
3
  A common use-case for applications is to have [Chef](http://www.opscode.com/chef/) configure your systems and use [Capistrano](http://capify.org/) to deploy the applications that run on them.
5
4
 
5
+ Capistrano Chef is a Capistrano extension that makes Chef and Capistrano get along like best buds.
6
+
7
+ ## Roles
8
+
6
9
  The Capistrano configuration has a facility to specify the roles for your application and which servers are members of those roles. Chef has its own roles. If you're using both Chef and Capistrano, you don't want to have to tell them both about which servers you'll be deploying to, especially if they change often.
7
10
 
8
11
  capistrano-chef provides some helpers to query your Chef server from Capistrano to define these roles.
9
12
 
10
- Examples
11
- --------
13
+ ### Examples
12
14
 
13
15
  A normal `deploy.rb` in an app using capistrano defines a roles like this:
14
16
 
15
- role :web, 10.0.0.1, 10.0.0.2
16
- role :db, 10.0.0.3, :primary => true
17
+ role :web, '10.0.0.2', '10.0.0.3'
18
+ role :db, '10.0.0.2', :primary => true
17
19
 
18
20
  Using capistrano-chef, you can do this:
19
21
 
20
22
  require 'capistrano/chef'
21
- chef_role :web "roles:web"
22
- chef_role :db, "roles:database_master", :primary => true,
23
- :attribute => :private_ip
23
+ chef_role :web 'roles:web'
24
+ chef_role :db, 'roles:database_master', :primary => true,
25
+ :attribute => :private_ip,
26
+ :limit => 1
27
+
28
+ Use a Hash to get a specific network interface:
29
+ (the Hash must be in the form of { 'interface-name' => 'network-family-name' })
30
+
31
+ chef_role :web, 'roles:web', :attribute => { :eth1 => :inet }
32
+
33
+ For a more deep and complex attribute search, use a Proc object:
34
+
35
+ (Example, to get 'eth1.inet.ipaddress': http://wiki.opscode.com/display/chef/Search#Search-SearchonlyreturnsIPAddressoftheNode%2Cnotofaspecificinterface)
36
+
37
+ chef_role :web, 'roles:web', :attribute => Proc.new do |n|
38
+ n["network"]["interfaces"]["eth1"]["addresses"].select{|address, data| data["family"] == "inet" }.keys.first
39
+ end
40
+
41
+ This defines the same roles using Chef's [search feature](http://wiki.opscode.com/display/chef/Search). Nodes are searched using the given query. The node's `ipaddress` attribute is used by default, but other attributes can be specified in the options as shown in the examples above. The rest of the options are the same as those used by Capistrano.
42
+
43
+ The `limit` attribute of the options hash will make it so only that the given number of items will be returned from a search.
44
+
45
+ ## Data Bags
46
+
47
+ Chef [Data Bags](http://wiki.opscode.com/display/chef/Data+Bags) let you store arbitrary JSON data. A common pattern is to use an _apps_ data bag to store data about an application for use in configuration and deployment.
48
+
49
+ Chef also has a [Deploy Resource](http://wiki.opscode.com/display/chef/Deploy+Resource) described in on of their blog posts, [Data Driven Application Deployment with Chef](http://www.opscode.com/blog/2010/05/06/data-driven-application-deployment-with-chef/). This is one method of deploying, but, if you're reading this, you're probably interested in deploying with Capistrano.
50
+
51
+ If you create an _apps_ data bag item (let's call it _myapp_), Capistrano Chef will let you use the data in your Capistrano recipes with the `set_from_data_bag` method.
52
+
53
+ This will allow you to store all of your metadata about your app in one place.
54
+
55
+ ### Example
56
+
57
+ In normal Capistrano `deploy.rb`:
58
+
59
+ set :application, 'myapp'
60
+ set :user, 'myapp'
61
+ set :deploy_to, '/var/apps/myapp'
62
+ set :scm, :git
63
+ ... # and so on
64
+
65
+ With Capistrano Chef, an _apps_ data bag item:
66
+
67
+ {
68
+ "id": "myapp",
69
+ "user": "myapp",
70
+ "deploy_to": "/var/apps/myapp",
71
+ "scm": "git",
72
+ ... // and so on
73
+ }
74
+
75
+ And in the`deploy.rb`:
76
+
77
+ set :application, 'myapp'
78
+ set_from_data_bag
24
79
 
25
- This defines the same roles using Chef's [search feature](http://wiki.opscode.com/display/chef/Search). Nodes are searched using the given query. The node's `ipaddress` attribute is used by default, but another (top-level) attribute can be specified in the options. The rest of the options are the same as those used by Capistrano.
80
+ If you want to use a data bag other than _apps_, you can do `set_from_data_bag :my_other_data_bag`.
26
81
 
27
- Chef configuration options are loaded by [Knife](http://wiki.opscode.com/display/chef/Knifehttp://wiki.opscode.com/display/chef/Knife), looking for `.chef/knife.rb` in the current directory or one its parent directories.
82
+ ## Chef Configuration
28
83
 
29
- To generate this file, run the following from the root of your project:
84
+ A Chef server is expected to be available and [Knife](http://wiki.opscode.com/display/chef/Knife) is used to configure the extension, looking for knife.rb the keys needed in .chef in the current directory or one its parent directories.
30
85
 
31
- $ knife configure -i
86
+ If you're using [Opscode Hosted Chef](http://www.opscode.com/hosted-chef/) these files will be provided for you. If not, the configuration can be generated with `knife configure -i`. See the [Chef Documentation](http://wiki.opscode.com/display/chef/Chef+Repository#ChefRepository-Configuration) for more details.
32
87
 
88
+ ## Requirements
33
89
 
90
+ Tested with Ruby Enterprise Edition 1.8.7, Ruby 1.9.2 and 1.9.3. Should work with Capistrano 2 or greater.
34
91
 
35
- License
36
- -------
92
+ ## License
37
93
 
38
- Copyright (c) 2011 Cramer Development, Inc.
94
+ Copyright (c) 2011-2012 Cramer Development, Inc.
39
95
 
40
96
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
41
97
 
data/Rakefile CHANGED
@@ -1,2 +1,9 @@
1
1
  require 'bundler'
2
+ require 'rspec/core/rake_task'
2
3
  Bundler::GemHelper.install_tasks
4
+
5
+ desc 'Default: run specs.'
6
+ task :default => :spec
7
+
8
+ desc 'Run specs'
9
+ RSpec::Core::RakeTask.new
@@ -19,6 +19,6 @@ Gem::Specification.new do |s|
19
19
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
20
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
21
  s.require_paths = ["lib"]
22
- s.add_dependency 'capistrano', '~> 2.9.0'
22
+ s.add_dependency 'capistrano', '>= 2'
23
23
  s.add_dependency 'chef', '~> 0.10.8'
24
24
  end
@@ -1,3 +1,3 @@
1
1
  module CapistranoChef
2
- VERSION = '0.0.2'.freeze
2
+ VERSION = '0.0.3'.freeze
3
3
  end
@@ -1,17 +1,61 @@
1
+ require 'capistrano'
1
2
  require 'chef/knife'
3
+ require 'chef/data_bag_item'
2
4
  require 'chef/search/query'
3
5
 
4
- Capistrano::Configuration.instance.load do
5
- Chef::Knife.new.configure_chef
6
+ module Capistrano::Chef
7
+ # Set up chef configuration
8
+ def self.configure_chef
9
+ knife = Chef::Knife.new
10
+ # If you don't do this it gets thrown into debug mode
11
+ knife.config = { :verbosity => 1 }
12
+ knife.configure_chef
13
+ end
14
+
15
+ # Do a search on the Chef server and return an attary of the requested
16
+ # matching attributes
17
+ def self.search_chef_nodes(query = '*:*', arg = :ipaddress, limit = 1000)
18
+ search_proc = \
19
+ case arg
20
+ when Proc
21
+ arg
22
+ when Hash
23
+ iface, family = arg.keys.first.to_s, arg.values.first.to_s
24
+ Proc.new do |n|
25
+ addresses = n["network"]["interfaces"][iface]["addresses"]
26
+ addresses.select{|address, data| data["family"] == family }.keys.first
27
+ end
28
+ when Symbol, String
29
+ Proc.new{|n| n[arg.to_s]}
30
+ else
31
+ raise ArgumentError, 'Search arguments must be Proc, Hash, Symbol, String.'
32
+ end
33
+ Chef::Search::Query.new.search(:node, query, 'X_CHEF_id_CHEF_X asc', 0, limit)[0].map(&search_proc)
34
+ end
35
+
36
+ def self.get_data_bag_item(id, data_bag = :apps)
37
+ Chef::DataBagItem.load(data_bag, id).raw_data
38
+ end
39
+
40
+ # Load into Capistrano
41
+ def self.load_into(configuration)
42
+ self.configure_chef
43
+ configuration.set :capistrano_chef, self
44
+ configuration.load do
45
+ def chef_role(name, query = '*:*', options = {})
46
+ role name, *(capistrano_chef.search_chef_nodes(query, options.delete(:attribute), options.delete(:limit)) + [options])
47
+ end
6
48
 
7
- # Define a role for capistrano, but instead of a list of addresses, use a chef
8
- # query to search nodes.
9
- def chef_role(name, query = "*:*", options = {})
10
- # TODO: This can only get a node's top-level attributes. Make it get nested
11
- # ones.
12
- attr = options.delete(:attribute) || :ipaddress
13
- nodes = Chef::Search::Query.new.search(:node, query)[0].map {|n| n[attr] }
14
- role name, *nodes, options
15
- nodes
49
+ def set_from_data_bag(data_bag = :apps)
50
+ raise ':application must be set' if fetch(:application).nil?
51
+ capistrano_chef.get_data_bag_item(application, data_bag).each do |k, v|
52
+ set k, v
53
+ end
54
+ end
55
+ end
16
56
  end
17
57
  end
58
+
59
+ if Capistrano::Configuration.instance
60
+ Capistrano::Chef.load_into(Capistrano::Configuration.instance)
61
+ end
@@ -0,0 +1,133 @@
1
+ require 'spec_helper'
2
+ require 'capistrano/chef'
3
+
4
+ MOCK_NODE_DATA = [{
5
+ "ipaddress" => '10.0.0.2',
6
+ "fqdn" => 'localhost.localdomain',
7
+ "hostname" => 'localhost',
8
+ "network" => {
9
+ "default_interface" => "eth0",
10
+ "interfaces" => {
11
+ "eth0" => {
12
+ "addresses" => {
13
+ "fe80::a00:27ff:feca:ab08" => {"scope" => "Link", "prefixlen" => "64", "family" => "inet6"},
14
+ "10.0.0.2" => {"netmask" => "255.255.255.0", "broadcast" => "10.0.0.255", "family" => "inet"},
15
+ "08:00:27:CA:AB:08" => {"family" => "lladdr"}
16
+ },
17
+ },
18
+ "lo" => {
19
+ "addresses" => {
20
+ "::1" => {"scope" => "Node", "prefixlen" => "128", "family" => "inet6"},
21
+ "127.0.0.1" => {"netmask" => "255.0.0.0", "family" => "inet"}
22
+ },
23
+ },
24
+ "eth1" => {
25
+ "addresses" => {
26
+ "fe80::a00:27ff:fe79:83fc" => {"scope" => "Link", "prefixlen" => "64", "family" => "inet6"},
27
+ "192.168.77.101" => {"netmask" => "255.255.255.0", "broadcast" => "192.168.77.255", "family" => "inet"},
28
+ "08:00:27:79:83:FC" => {"family" => "lladdr"}
29
+ },
30
+ },
31
+ },
32
+ }
33
+ }]
34
+
35
+ describe Capistrano::Chef do
36
+ before do
37
+ # Stub knife config
38
+ @knife = mock('Chef::Knife')
39
+ Chef::Knife.stub!(:new).and_return(@knife)
40
+ @knife.stub!(:configure_chef)
41
+ @knife.stub!(:config=)
42
+
43
+ # Load into capistrano configuration
44
+ @configuration = Capistrano::Configuration.new
45
+ Capistrano::Chef.load_into(@configuration)
46
+
47
+ # Data bag items
48
+ @other_item = mock('Chef::DataBagItem')
49
+ Chef::DataBagItem.stub(:load).with(:other_data_bag, 'other_test').and_return @other_item
50
+ @other_item.stub(:raw_data).and_return Mash.new({
51
+ :id => 'other_test',
52
+ :deploy_to => '/dev/other_null'
53
+ })
54
+
55
+ @item = mock('Chef::DataBagItem')
56
+ Chef::DataBagItem.stub(:load).with(:apps, 'test').and_return @item
57
+ @item.stub(:raw_data).and_return Mash.new({
58
+ :id => 'test',
59
+ :deploy_to => '/dev/null'
60
+ })
61
+ end
62
+
63
+ it 'should be a module' do
64
+ expect { described_class.to be_a Module }
65
+ end
66
+
67
+
68
+ describe 'search_chef_nodes' do
69
+ before(:each) do
70
+ Chef::Knife.new.configure_chef
71
+ @search = mock('Chef::Search::Query')
72
+ Chef::Search::Query.stub!(:new).and_return(@search)
73
+ @search.stub!(:search).and_return([::MOCK_NODE_DATA, 0, 1])
74
+ end
75
+
76
+ specify 'without argument (will get :ipaddress)' do
77
+ Capistrano::Chef.search_chef_nodes('*:*').should eql ['10.0.0.2']
78
+ end
79
+
80
+ # with Symbol(or String) will search top-level attributes
81
+ specify 'with Symbol argument' do
82
+ Capistrano::Chef.search_chef_nodes('*:*', :fqdn).should eql ['localhost.localdomain']
83
+ end
84
+
85
+ # with Hash, can specify "interface" and "family" by key and value.
86
+ specify 'with Hash argument' do
87
+ Capistrano::Chef.search_chef_nodes('*:*', {:eth0 => :inet}).should eql ['10.0.0.2']
88
+ end
89
+
90
+ # use Proc for more deep, complex attributes search.
91
+ specify 'with Proc argument' do
92
+ search_proc = Proc.new do |n|
93
+ n["network"]["interfaces"]["eth1"]["addresses"].select{|address, data| data["family"] == "inet" }.keys.first
94
+ end
95
+ Capistrano::Chef.search_chef_nodes('*:*', search_proc).should eql ['192.168.77.101']
96
+ end
97
+ end
98
+
99
+
100
+ specify 'get_data_bag_item' do
101
+ Capistrano::Chef.get_data_bag_item('test').should === Mash.new({
102
+ :id => 'test',
103
+ :deploy_to => '/dev/null'
104
+ })
105
+ Capistrano::Chef.get_data_bag_item('other_test', :other_data_bag).should === Mash.new({
106
+ :id => 'other_test',
107
+ :deploy_to => '/dev/other_null'
108
+ })
109
+ end
110
+
111
+ specify 'set_from_data_bag' do
112
+ expect { @configuration.set_from_data_bag }.to raise_error
113
+ @configuration.set(:application, 'test')
114
+ @configuration.set_from_data_bag
115
+ @configuration.fetch(:deploy_to).should === '/dev/null'
116
+ @configuration.fetch(:id).should === 'test'
117
+
118
+ @configuration.set(:application, 'other_test')
119
+ @configuration.set_from_data_bag :other_data_bag
120
+ @configuration.fetch(:deploy_to).should === '/dev/other_null'
121
+ @configuration.fetch(:id).should === 'other_test'
122
+ end
123
+
124
+ specify 'chef_role' do
125
+ Capistrano::Chef.stub!(:search_chef_nodes).and_return(['10.0.0.2'])
126
+ @search = mock('Chef::Search::Query')
127
+ @configuration.should respond_to :chef_role
128
+
129
+ @configuration.chef_role(:test)
130
+ @configuration.roles.should have_key :test
131
+ @configuration.roles[:test].to_a[0].host.should === '10.0.0.2'
132
+ end
133
+ end
@@ -0,0 +1,11 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper.rb"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capistrano-chef
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,22 +9,27 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-10 00:00:00.000000000Z
12
+ date: 2012-06-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: capistrano
16
- requirement: &70339838004600 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
- - - ~>
19
+ - - ! '>='
20
20
  - !ruby/object:Gem::Version
21
- version: 2.9.0
21
+ version: '2'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70339838004600
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '2'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: chef
27
- requirement: &70339838004040 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ~>
@@ -32,7 +37,12 @@ dependencies:
32
37
  version: 0.10.8
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *70339838004040
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.10.8
36
46
  description: Allows capistrano to use Chef data for deployment
37
47
  email:
38
48
  - nlloyds@gmail.com
@@ -41,12 +51,17 @@ extensions: []
41
51
  extra_rdoc_files: []
42
52
  files:
43
53
  - .gitignore
54
+ - .rspec
55
+ - .travis.yml
44
56
  - Gemfile
57
+ - Guardfile
45
58
  - README.md
46
59
  - Rakefile
47
60
  - capistrano-chef.gemspec
48
61
  - lib/capistrano/chef.rb
49
62
  - lib/capistrano/chef/version.rb
63
+ - spec/capistrano/chef_spec.rb
64
+ - spec/spec_helper.rb
50
65
  homepage: https://github.com/cramerdev/capistrano-chef
51
66
  licenses:
52
67
  - MIT
@@ -60,17 +75,24 @@ required_ruby_version: !ruby/object:Gem::Requirement
60
75
  - - ! '>='
61
76
  - !ruby/object:Gem::Version
62
77
  version: '0'
78
+ segments:
79
+ - 0
80
+ hash: 3940310999397509065
63
81
  required_rubygems_version: !ruby/object:Gem::Requirement
64
82
  none: false
65
83
  requirements:
66
84
  - - ! '>='
67
85
  - !ruby/object:Gem::Version
68
86
  version: '0'
87
+ segments:
88
+ - 0
89
+ hash: 3940310999397509065
69
90
  requirements: []
70
91
  rubyforge_project: capistrano-chef
71
- rubygems_version: 1.8.15
92
+ rubygems_version: 1.8.24
72
93
  signing_key:
73
94
  specification_version: 3
74
95
  summary: Capistrano extensions for Chef integration
75
- test_files: []
76
- has_rdoc:
96
+ test_files:
97
+ - spec/capistrano/chef_spec.rb
98
+ - spec/spec_helper.rb