blender-chef 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0678c7aeb1d0d4a93bc0a1cf94807248d3860b7a
4
- data.tar.gz: 6a3369819b869119d59021f45d65af65ff380858
3
+ metadata.gz: 227b93ae896e5908d4a492d8bb0962652810ebcb
4
+ data.tar.gz: dd20886e6a0cd3691853ea131763cd482b9dfc89
5
5
  SHA512:
6
- metadata.gz: a931c089da34be8cc2811ee8f1a7b0c266307967f0ab8932c31476bebe1e1e9e4480247c3d7faefe17644d152495695fb870d3d983f379be3ce0a24ebc16ab92
7
- data.tar.gz: ac3bb2c4e26e32671b44120bd60b0c98923149631c9673f06482d3a42dac2560a23ae46625dd84cbcfbcad466887a3bf12632519f0d94a26749b7609071f8a16
6
+ metadata.gz: 4f6879284bbb5c572d8f3c4f9f203216e195866a236334381b33f6201893a023be4146cf2f81577e723e3b60d8e67c5614f9557ae790b1c6717b59b5fba5b90d
7
+ data.tar.gz: 47bf4c4b37718ecd3611f6b9da590eac1682a862979fe016efbc8029cc9aae2187f98d0f8ef81270e7ff2426b9c95ac889984512c36e4686285799882237a4f9
@@ -2,7 +2,7 @@ before_install:
2
2
  - bundle install --path .bundle
3
3
  rvm:
4
4
  - 2.1.0
5
- - 2.1.2
5
+ - 2.2.0
6
6
  branches:
7
7
  only:
8
8
  - master
data/README.md CHANGED
@@ -14,7 +14,7 @@ fetched from Chef server. Following is an example of dynamically obtaining
14
14
  all servers with `db` role, and enlisting their `iptables` rule.
15
15
 
16
16
  ```ruby
17
- require 'belnder/chef'
17
+ require 'blender/chef'
18
18
  config(:chef, chef_sever_url: 'https://foo.bar.com', node_name: 'admin', client_key: 'admin.pem')
19
19
  members(search(:chef, 'roles:db'))
20
20
  ssh_task 'sudo iptables -L'
@@ -40,6 +40,67 @@ config(:chef, node_name: 'admin', client_key: 'admin.pem')
40
40
  members(search(:chef, 'roles:db', attribute: 'ec2_public_ipv4'))
41
41
  ```
42
42
 
43
+ ### Knife integration
44
+
45
+ Blender-chef comes with a knife plugin that allows job or chef recipe or node bootstrapping
46
+ from command line interface.
47
+
48
+ - Blender-mode, for running raw blender jobs
49
+ Given the following job present in file `jobs/upgrade_chef.rb`
50
+
51
+ ```ruby
52
+ ssh_task 'sudo apt-get remove chef --purge'
53
+ ssh_task 'sudo apt-get remove chef --purge'
54
+ ssh_task 'wget -c https://opscode-omnibus-packages.s3.amazonaws.com/ubuntu/13.04/x86_64/chef_12.0.3-1_amd64.deb'
55
+ ssh_task 'sudo dpkg -i chef_12.0.3-1_amd64.deb'
56
+ ssh_task 'sudo chef-client'
57
+ ```
58
+
59
+ It can be executed against all chef nodes having `db` role as:
60
+
61
+ ```sh
62
+ knife blend --search 'roles:db' jobs/upgrade_chef.rb
63
+ ```
64
+ this is equivalent to
65
+ ```sh
66
+ knife blend --mode blender --search 'roles:db' jobs/upgrade_chef.rb
67
+ ```
68
+ as blender mode is the default mode.
69
+
70
+ - Recipe mode, for running one-off chef recipes against remote nodes using blender
71
+ Given the following chef recipe present ina local file `recipes/nginx.rb`
72
+
73
+ ```ruby
74
+ package 'nginx'
75
+ service 'nginx' do
76
+ action [:start, :enable]
77
+ end
78
+ ```
79
+
80
+ It can be executed against all chef nodes having `web` role as:
81
+
82
+ ```sh
83
+ knife blend --mode recipe --search 'roles:web' recipes/nginx.rb
84
+ ```
85
+ In recipe mode blender will treat the input file(s) as chef recipe
86
+ and build necessary scp and ssh tasks to upload the recipe, execute it
87
+ and remove the uploaded recipe.
88
+
89
+ - Berkshelf mode, for bootstrapping server with chef solo. In this mode blender-chef
90
+ will assume the specifid file is a Berksfile, and use it to vendor cookbooks, then
91
+ upload it to the remote server using scp, and finally run chef in localmode against
92
+ the vendored cookbook directory. In berkshelf mode run list is specified via `--run-list`
93
+ option.
94
+
95
+ ```sh
96
+ knife blend Berksfile --mode berkshelf -h foo.bar.com -i id_rsa --run-list recipe[nginx]
97
+ ```
98
+
99
+ Note: You have to install berkshelf explicitly (vie bundler or using raw gem commands).
100
+
101
+
102
+ Additional options are provided to control strategy, ssh credentials etc.
103
+
43
104
 
44
105
  ## Supported ruby versions
45
106
 
@@ -47,7 +108,6 @@ Blender-chef uses Chef 12 (for partial search). For chef 11, use 0.0.1 version o
47
108
 
48
109
  Blender-chef currently support the following MRI versions:
49
110
 
50
- * *Ruby 1.9.3*
51
111
  * *Ruby 2.1.0*
52
112
  * *Ruby 2.1.2*
53
113
 
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = 'blender-chef'
7
- spec.version = '0.1.0'
7
+ spec.version = '0.2.0'
8
8
  spec.authors = ['Ranjib Dey']
9
9
  spec.email = ['ranjib@pagerduty.com']
10
10
  spec.summary = %q{Chef search based host discovery for blender}
@@ -16,8 +16,8 @@ Gem::Specification.new do |spec|
16
16
  spec.test_files = spec.files.grep(%r{^spec/})
17
17
  spec.require_paths = ['lib']
18
18
 
19
- spec.add_dependency 'pd-blender'
20
- spec.add_dependency 'chef', '>= 12.0.0'
19
+ spec.add_dependency 'pd-blender', '>= 0.1.0'
20
+ spec.add_dependency 'chef', '>= 12.1.1'
21
21
 
22
22
  spec.add_development_dependency 'bundler'
23
23
  spec.add_development_dependency 'rake'
@@ -44,7 +44,7 @@ module Blender
44
44
  ::Chef::Config[:chef_server_url] = options[:chef_server_url] if options[:chef_server_url]
45
45
  q = ::Chef::Search::Query.new
46
46
  res = q.search(:node, search_term, filter_result: {attribute: attr.split('.')})
47
- res.first.collect{|node_data| node_data['data']['attribute']}
47
+ res.first.collect{|node_data| node_data['attribute']}
48
48
  end
49
49
  end
50
50
  end
@@ -53,6 +53,12 @@ class Chef
53
53
  long: '--noop',
54
54
  description: 'no-op aka dry-run mode, run blender without executing jobs'
55
55
 
56
+ option :prompt,
57
+ default: false,
58
+ boolean: true,
59
+ long: '--prompt',
60
+ description: 'Prompt for password'
61
+
56
62
  option :quiet,
57
63
  default: false,
58
64
  boolean: true,
@@ -93,17 +99,16 @@ class Chef
93
99
  long: '--identity-file IDENTITY_FILE',
94
100
  description: 'Identity file for SSH authentication'
95
101
 
96
- option :recipe_mode,
97
- long: '--recipe-mode',
98
- description: 'Treat input files as chef recipe and compose blender tasks to execute them (scp + ssh)',
99
- boolean: true,
100
- default: false
101
-
102
- option :recipe_mode,
103
- long: '--recipe-mode',
104
- description: 'Treat input files as chef recipe and compose blender tasks to execute them (scp + ssh)',
105
- boolean: true,
106
- default: false
102
+ desc = "Run mode. Can be 'blender', 'recipe' or 'berkshelf'\n"
103
+ desc << "In 'blender' mode input file is treated as a Blender job\n"
104
+ desc << "In 'recipe' mode input file is treated as an individual recipe and executed using chef-apply\n"
105
+ desc << "In 'berkshelf' mode input file is treated as a Berksfile. Blender vendors cookbook using berksshelf, scp it and run chef against it in localmode\n"
106
+ option :mode,
107
+ long: '--mode MODE',
108
+ short: '-m MODE',
109
+ description: desc,
110
+ default: :blender,
111
+ proc: lambda{|s| s.to_sym}
107
112
 
108
113
  option :chef_apply,
109
114
  long: '--chef-apply',
@@ -111,54 +116,123 @@ class Chef
111
116
  description: 'chef-apply command to be used (effective only in recipe mode)',
112
117
  default: 'chef-apply'
113
118
 
119
+ option :run_list,
120
+ long: '--run-list RUN_LIST',
121
+ short: '-r RUN_LIST',
122
+ description: 'chef-apply command to be used (effective only in berkshelf mode)'
123
+
124
+ option :hosts,
125
+ long: '--hosts HOST1,HOST2,HOST3',
126
+ short: '-h HOST1,HOST2,HOST3',
127
+ description: 'Pass hosts manually (search and attribute option will be ignored)'
128
+
114
129
  def run
115
- ssh_options = {
116
- user: config[:user],
117
- stdout: $stdout
118
- }
119
- ssh_options[:stdout] = $stdout if config[:stream]
120
- if config[:password]
121
- ssh_options[:password] = config[:password]
122
- elsif config[:prompt]
123
- ssh_options[:password] = ui.ask('SSH password: ') {|q|q.echo = false}
124
- end
125
- if config[:identity_file]
126
- ssh_options[:keys] = Array(config[:identity_file])
127
- end
128
130
  scheduler_options = {
129
131
  config_file: config[:blender_config],
130
132
  no_doc: config[:quiet]
131
133
  }
134
+
132
135
  discovery_options = {
133
136
  attribute: config[:attribute]
134
137
  }
138
+
135
139
  Blender::Configuration[:noop] = config[:noop]
136
- members = Blender::Discovery::Chef.new(discovery_options).search(config[:search])
137
-
138
- @name_args.each do |file|
139
- if config[:recipe_mode]
140
- remote_path = File.join('/tmp', SecureRandom.hex(10))
141
- Blender.blend(options[:file], scheduler_options) do |scheduler|
142
- scheduler.strategy(config[:strategy])
143
- scheduler.config(:ssh, ssh_options)
144
- scheduler.config(:scp, ssh_options)
145
- scheduler.members(members)
146
- scheduler.scp_upload(remote_path) do
147
- from file
140
+
141
+ if config[:hosts]
142
+ members = config[:hosts].split(',')
143
+ else
144
+ members = Blender::Discovery::Chef.new(discovery_options).search(config[:search])
145
+ end
146
+ ssh_config = ssh_options
147
+ Blender.blend('blender-chef', scheduler_options) do |scheduler|
148
+ scheduler.strategy(config[:strategy])
149
+ scheduler.config(:ssh, ssh_config)
150
+ scheduler.config(:scp, ssh_config)
151
+ scheduler.members(members)
152
+ @name_args.each do |file|
153
+ case config[:mode]
154
+ when :berkshelf
155
+ begin
156
+ require 'berkshelf'
157
+ rescue LoadError
158
+ raise RuntimeError, 'You must install berkshelf before using blender-chef in berkshelf mode'
148
159
  end
149
- scheduler.ssh_task "#{config[:chef_apply]} #{remote_path}"
150
- scheduler.ssh_task "rm #{remote_path}"
160
+ tempdir = Dir.mktmpdir
161
+ berkshelf_mode(scheduler, tempdir, file)
162
+ FileUtils.rm_rf(tempdir)
163
+ when :recipe
164
+ recipe_mode(scheduler, file)
165
+ when :blender
166
+ blender_mode(scheduler, file)
167
+ else
168
+ raise ArgumentError, "Unknown mode: '#{config[:mode]}'"
151
169
  end
152
- else
153
- job = File.read(file)
154
- Blender.blend(options[:file], scheduler_options) do |scheduler|
155
- scheduler.strategy(config[:strategy])
156
- scheduler.config(:ssh, ssh_options)
157
- scheduler.members(members)
158
- scheduler.instance_eval(job, __FILE__, __LINE__)
170
+ end
171
+ end
172
+ end
173
+
174
+ def ssh_options
175
+ opts = {
176
+ user: config[:user]
177
+ }
178
+ if config[:identity_file]
179
+ opts[:keys] = Array(config[:identity_file])
180
+ end
181
+ if config[:stream] or (!config[:quiet])
182
+ opts[:stdout] = $stdout
183
+ end
184
+ if config[:password]
185
+ opts[:password] = config[:password]
186
+ elsif config[:prompt]
187
+ opts[:password] = ui.ask('SSH password: ') {|q|q.echo = false}
188
+ end
189
+ opts
190
+ end
191
+
192
+ def berkshelf_mode(scheduler, tempdir, file)
193
+ run_list = config[:run_list]
194
+ scheduler.ruby_task 'generate cookbook tarball' do
195
+ execute do
196
+ berksfile = Berkshelf::Berksfile.from_file('Berksfile')
197
+ berksfile.vendor(tempdir)
198
+ File.open('/tmp/solo.rb', 'w') do |f|
199
+ f.write("cookbook_path '/tmp/cookbooks'\n")
200
+ f.write("file_cache_path '/var/cache/chef/cookbooks'\n")
159
201
  end
160
202
  end
161
203
  end
204
+ scheduler.ssh_task 'nuke old cookbook directory if exist' do
205
+ execute 'rm -rf /tmp/cookbooks'
206
+ end
207
+ scheduler.scp_upload 'upload cookbooks' do
208
+ from tempdir
209
+ to '/tmp/cookbooks'
210
+ recursive true
211
+ end
212
+ scheduler.scp_upload '/tmp/solo.rb' do
213
+ from '/tmp/solo.rb'
214
+ end
215
+ scheduler.ssh_task 'create cache directory' do
216
+ execute 'sudo mkdir -p /var/cache/chef/cookbooks'
217
+ end
218
+ scheduler.ssh_task 'run chef solo' do
219
+ execute "sudo chef-client -z -o #{run_list} -c /tmp/solo.rb --force-logger"
220
+ end
221
+ end
222
+
223
+ def recipe_mode(scheduler, file)
224
+ remote_path = File.join('/tmp', SecureRandom.hex(10))
225
+ scheduler.scp_upload('upload recipe') do
226
+ to remote_path
227
+ from file
228
+ end
229
+ scheduler.ssh_task "#{config[:chef_apply]} #{remote_path}"
230
+ scheduler.ssh_task "rm #{remote_path}"
231
+ end
232
+
233
+ def blender_mode(scheduler, file)
234
+ job = File.read(file)
235
+ scheduler.instance_eval(job, __FILE__, __LINE__)
162
236
  end
163
237
  end
164
238
  end
@@ -1,6 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  require 'chef/knife/blender'
4
+ require 'blender/rspec'
4
5
 
5
6
  describe Chef::Knife::Blend do
6
7
  before(:each) do
@@ -10,20 +11,24 @@ describe Chef::Knife::Blend do
10
11
  @knife.config[:passsword] = 'test-password'
11
12
  @knife.config[:search] = 'roles:db'
12
13
  @knife.config[:strategy] = :default
13
- @knife.name_args = ["job.rb"]
14
14
  end
15
15
  it '#non recipe mode' do
16
- disco = double(Blender::Discovery::Chef)
17
- expect(Blender::Discovery::Chef).to receive(:new).and_return(disco)
18
- expect(disco).to receive(:search).and_return(['host1', 'host2'])
16
+ @knife.name_args = ["job.rb"]
17
+ stub_search(:chef, 'roles:db').and_return(%w(host1 host2))
19
18
  expect(File).to receive(:read).with('job.rb').and_return('')
19
+ @knife.config[:mode] = :blender
20
20
  @knife.run
21
21
  end
22
22
  it '#recipe mode' do
23
- @knife.config[:recipe_mode] = true
24
- disco = double(Blender::Discovery::Chef)
25
- expect(Blender::Discovery::Chef).to receive(:new).and_return(disco)
26
- expect(disco).to receive(:search).and_return([])
23
+ @knife.name_args = ["job.rb"]
24
+ @knife.config[:mode] = :recipe
25
+ stub_search(:chef, 'roles:db').and_return([])
26
+ @knife.run
27
+ end
28
+ it '#berkshelf mode' do
29
+ @knife.name_args = ['/path/to/berksfile']
30
+ @knife.config[:mode] = :recipe
31
+ stub_search(:chef, 'roles:db').and_return([])
27
32
  @knife.run
28
33
  end
29
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blender-chef
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ranjib Dey
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-02 00:00:00.000000000 Z
11
+ date: 2015-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pd-blender
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 0.1.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 0.1.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: chef
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 12.0.0
33
+ version: 12.1.1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 12.0.0
40
+ version: 12.1.1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement