blender-chef 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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