chef-provisioning-ssh 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6aae7c38cadd63e7e9f24c09b92e70a2ce89f4db
4
+ data.tar.gz: b5e34e1c06a4eb66dfbf553ebf374aa08673d0ce
5
+ SHA512:
6
+ metadata.gz: de89311eb5a7e788650dc0ec9e277472c582c721a8799bbe4b7e14345e5470c89db760de0184e4823c68d2a1f6915291a28bb542bbb844a9e6bf1ad224d34d71
7
+ data.tar.gz: 745f02e2a483c1ffe38523782fc46219684ca32c2d3dd041af26d59a64802fe025cc371d3f57e49c69614b6ac365e7b67ad0aa4e8d34905914a373c2f188a092
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 double-z
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,148 @@
1
+ [![Gem Version](https://badge.fury.io/rb/chef-provisioning-ssh.svg)](http://badge.fury.io/rb/chef-provisioning-ssh)
2
+
3
+ # Chef::Provisioning::Ssh
4
+
5
+ TODO: Write a gem description
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+ e
11
+ ```ruby
12
+ gem 'chef-provisioning-ssh'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install chef-provisioning-ssh
22
+
23
+ ## Usage
24
+
25
+ The `machine_options` for provisioning ssh now use the key `transport_options` which line up directly with the `transport_options` for chef-provisioning proper. Transport_options can be a string or symbol and will be properly converted. The transport_options can be viewed in the code for chef-provisioning here:
26
+
27
+ https://github.com/chef/chef-provisioning/blob/master/lib/chef/provisioning/transport/ssh.rb#L17-L34
28
+
29
+ The snippet from that link is:
30
+
31
+ - host: the host to connect to, e.g. '145.14.51.45'
32
+ - username: the username to connect with
33
+ - ssh_options: a list of options to Net::SSH.start
34
+ - options: a hash of options for the transport itself, including:
35
+ - :prefix: a prefix to send before each command (e.g. "sudo ")
36
+ - :ssh_pty_enable: set to false to disable pty (some instances don't
37
+ support this, most do)
38
+ - :ssh_gateway: the gateway to use, e.g. "jkeiser@145.14.51.45:222".
39
+ nil (the default) means no gateway. If the username is omitted,
40
+ then the default username is used instead (i.e. the user running
41
+ chef, or the username configured in .ssh/config).
42
+
43
+ The options are used in
44
+ Net::SSH.start(host, username, ssh_options)
45
+
46
+ In addition to host, ip_address and hostname are also additional options.
47
+
48
+ * valid ssh options are:
49
+
50
+ :auth_methods,
51
+ :bind_address,
52
+ :compression,
53
+ :compression_level,
54
+ :config,
55
+ :encryption,
56
+ :forward_agent,
57
+ :hmac,
58
+ :host_key,
59
+ :keepalive,
60
+ :keepalive_interval,
61
+ :kex,
62
+ :keys,
63
+ :key_data,
64
+ :languages,
65
+ :logger,
66
+ :paranoid,
67
+ :password,
68
+ :port,
69
+ :proxy,
70
+ :rekey_blocks_limit,
71
+ :rekey_limit,
72
+ :rekey_packet_limit,
73
+ :timeout,
74
+ :verbose,
75
+ :global_known_hosts_file,
76
+ :user_known_hosts_file,
77
+ :host_key_alias,
78
+ :host_name,
79
+ :user,
80
+ :properties,
81
+ :passphrase,
82
+ :keys_only,
83
+ :max_pkt_size,
84
+ :max_win_size, :send_env,
85
+ :use_agent
86
+
87
+ * machine resource example:
88
+
89
+ require 'chef/provisioning/ssh_driver'
90
+
91
+ with_driver 'ssh'
92
+
93
+ machine "sshone" do
94
+ action [:ready, :setup, :converge]
95
+ machine_options 'transport_options' => {
96
+ 'ip_address' => '192.168.33.22',
97
+ 'username' => 'vagrant',
98
+ 'ssh_options' => {
99
+ 'password' => 'vagrant'
100
+ }
101
+ }
102
+ recipe 'vagrant::sshone'
103
+ converge true
104
+ end
105
+
106
+ machine "sshtwo" do
107
+ action [:ready, :setup, :converge]
108
+ machine_options :transport_options => {
109
+ 'ip_address' => '192.168.33.23',
110
+ 'username' => 'vagrant',
111
+ 'ssh_options' => {
112
+ 'keys' => ['/home/username/.vagrant.d/insecure_private_key']
113
+ }
114
+ }
115
+ recipe 'vagrant::sshtwo'
116
+ converge true
117
+ end
118
+
119
+
120
+ To test it out, clone the repo:
121
+
122
+ `git clone https://github.com/double-z/chef-provisioning-ssh.git`
123
+
124
+ in the test directory there is a Vagrantfile with 2 nodes.
125
+
126
+ Run:
127
+
128
+ `vagrant up`
129
+
130
+ which will bring up both nodes.
131
+
132
+ Then run from the test directory:
133
+
134
+ `chef-client -z -o vagrant::test_provisioning_ssh`
135
+
136
+ This will run chef-provisioning on each of the two vagrant nodes.
137
+
138
+ thats it.
139
+
140
+ party on wayne.
141
+
142
+ ## Contributing
143
+
144
+ 1. Fork it ( http://github.com/double-z/chef-provisioning-ssh/fork )
145
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
146
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
147
+ 4. Push to the branch (`git push origin my-new-feature`)
148
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,34 @@
1
+ require 'chef/provider/lwrp_base'
2
+ require 'cheffish'
3
+
4
+ class Chef::Provider::SshCluster < Chef::Provider::LWRPBase
5
+
6
+ use_inline_resources
7
+
8
+ def whyrun_supported?
9
+ true
10
+ end
11
+
12
+ action :create do
13
+ the_base_path = new_resource.path
14
+ Cheffish.inline_resource(self, :create) do
15
+ directory the_base_path do
16
+ recursive true
17
+ end
18
+ end
19
+ end
20
+
21
+ action :delete do
22
+ the_base_path = new_resource.path
23
+ Cheffish.inline_resource(self, :delete) do
24
+ directory the_base_path do
25
+ recursive true
26
+ action :delete
27
+ end
28
+ end
29
+ end
30
+
31
+ def load_current_resource
32
+ end
33
+
34
+ end
@@ -0,0 +1,3 @@
1
+ require 'chef/provisioning/ssh_driver/driver'
2
+
3
+ Chef::Provisioning.register_driver_class('ssh', Chef::Provisioning::SshDriver::Driver)
@@ -0,0 +1,22 @@
1
+ require 'chef/provisioning'
2
+ require 'chef/resource/ssh_cluster'
3
+ require 'chef/provider/ssh_cluster'
4
+ require 'chef/provisioning/ssh_driver/version'
5
+ require 'chef/provisioning/ssh_driver/driver'
6
+
7
+ class Chef
8
+ module Provisioning
9
+ module SshDriver
10
+ end
11
+ end
12
+ end
13
+
14
+ class Chef
15
+ module DSL
16
+ module Recipe
17
+ def with_ssh_cluster(cluster_path, &block)
18
+ with_driver("ssh:#{cluster_path}", &block)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,475 @@
1
+ require 'json'
2
+ require 'resolv'
3
+ require 'chef/provisioning/driver'
4
+ require 'chef/provisioning/version'
5
+ require 'chef/provisioning/machine/basic_machine'
6
+ require 'chef/provisioning/machine/unix_machine'
7
+ require 'chef/provisioning/convergence_strategy/install_cached'
8
+ require 'chef/provisioning/transport/ssh'
9
+ require 'chef/provisioning/ssh_driver/version'
10
+ require 'chef/provisioning/ssh_driver/helpers'
11
+ require 'chef/resource/ssh_cluster'
12
+ require 'chef/provider/ssh_cluster'
13
+
14
+ class Chef
15
+ module Provisioning
16
+ module SshDriver
17
+ # Provisions Machines Using SSH.
18
+ class Driver < Chef::Provisioning::Driver
19
+ include Chef::Provisioning::SshDriver::Helpers
20
+ # ## Parameters
21
+ # cluster_path - path to the directory containing the vagrant files, which
22
+ # should have been created with the vagrant_cluster resource.
23
+
24
+ # Create a new ssh driver.
25
+ #
26
+ # ## Parameters
27
+ # cluster_path - path to the directory containing the vagrant files, which
28
+ # should have been created with the vagrant_cluster resource.
29
+ def initialize(driver_url, config)
30
+ super
31
+ scheme, cluster_path = driver_url.split(':', 2)
32
+ @cluster_path = cluster_path
33
+ end
34
+
35
+ attr_reader :cluster_path
36
+
37
+ def self.from_url(driver_url, config)
38
+ Driver.new(driver_url, config)
39
+ end
40
+
41
+ def self.canonicalize_url(driver_url, config)
42
+ scheme, cluster_path = driver_url.split(':', 2)
43
+ cluster_path = File.expand_path(cluster_path || File.join(Chef::Config.config_dir, 'provisioning/ssh'))
44
+ "ssh:#{cluster_path}"
45
+ end
46
+
47
+ def allocate_machine(action_handler, machine_spec, machine_options)
48
+ ensure_ssh_cluster(action_handler) unless ::File.exists?(cluster_path)
49
+
50
+ log_info("SSH Driver - allocate_machine - machine_spec = #{machine_spec.inspect}")
51
+ log_info("SSH Driver - allocate_machine - machine_options = #{machine_options['transport_options']}")
52
+ log_info("SSH Driver - allocate_machine - machine_options = #{machine_options.configs}")
53
+
54
+ new_machine = false
55
+ new_machine_options = {}
56
+ current_machine_options = false
57
+
58
+ if machine_options[:transport_options]
59
+ new_machine_options['transport_options'] = machine_options[:transport_options]
60
+ elsif machine_options['transport_options']
61
+ new_machine_options['transport_options'] = machine_options['transport_options']
62
+ end
63
+
64
+ if machine_options[:convergence_options]
65
+ new_machine_options['convergence_options'] = machine_options[:convergence_options]
66
+ elsif machine_options['convergence_options']
67
+ new_machine_options['convergence_options'] = machine_options['convergence_options']
68
+ end
69
+
70
+ if machine_spec.location && ssh_machine_exists?(machine_spec.name)
71
+ _current_machine_options = existing_machine_hash(machine_spec)
72
+ current_machine_options = stringify_keys(_current_machine_options.dup)
73
+ end
74
+
75
+ log_info "machine_spec.name #{machine_spec.name}"
76
+
77
+ log_info "new_machine_options #{new_machine_options} \n\n current_machine_options #{current_machine_options}"
78
+
79
+
80
+ machine_file_hash = updated_ssh_machine_file_hash(stringify_keys(new_machine_options),
81
+ stringify_keys(current_machine_options))
82
+
83
+ raise 'machine File Hash Is Empty' unless machine_file_hash
84
+ log_info("machine HASH = #{machine_file_hash}")
85
+
86
+ if machine_file_hash && machine_file_hash['transport_options']
87
+ host_for(machine_file_hash['transport_options'])
88
+ initialize_ssh(machine_file_hash['transport_options'])
89
+ end
90
+
91
+ machine_updated = create_ssh_machine_file(action_handler,
92
+ machine_spec.name,
93
+ machine_file_hash)
94
+ machine_options_for(machine_file_hash)
95
+
96
+ log_info("STRIPPED machine HASH = #{machine_file_hash}")
97
+ log_info("UNSTRIPPED machine HASH = #{machine_file_hash}")
98
+ log_info "machine_options_for #{machine_options_for}"
99
+
100
+ if machine_updated || !machine_spec.location
101
+ machine_spec.location = {
102
+ 'driver_url' => driver_url,
103
+ 'driver_version' => Chef::Provisioning::SshDriver::VERSION,
104
+ 'target_name' => machine_spec.name,
105
+ 'ssh_file_path' => "#{cluster_path}/#{machine_spec.name}.json",
106
+ 'allocated_at' => Time.now.utc.to_s
107
+ }
108
+ log_info("machine_spec.location= #{machine_spec.location}")
109
+ end
110
+ end
111
+
112
+ def strip_hash_nil(val)
113
+ vvv = case val
114
+ when Hash
115
+ cleaned_val = val.delete_if { |kk,vv| vv.nil? || (vv.is_a?(String) && vv.empty?) }
116
+ cleaned_val.each do |k,v|
117
+ case v
118
+ when Hash
119
+ strip_hash_nil(v)
120
+ when Array
121
+ v.flatten!
122
+ v.uniq!
123
+ end
124
+ end
125
+ end
126
+ # puts "VVV is #{vvv}"
127
+ vvv
128
+ end
129
+
130
+ def ready_machine(action_handler, machine_spec, machine_options)
131
+ allocate_machine(action_handler, machine_spec, machine_options)
132
+ machine_for(machine_spec, machine_options_for)
133
+ end
134
+
135
+ def connect_to_machine(machine_spec, machine_options)
136
+ allocate_machine(action_handler, machine_spec, machine_options)
137
+ machine_for(machine_spec, machine_options_for)
138
+ end
139
+
140
+ def destroy_machine(action_handler, machine_spec, machine_options)
141
+ delete_ssh_machine_file(action_handler, existing_ssh_machine_file(machine_spec.name))
142
+ # allocate_machine(action_handler, machine_spec, machine_options)
143
+ # convergence_strategy_for(node).delete_chef_objects(action_handler, node)
144
+ end
145
+
146
+ def stop_machine(action_handler, machine_spec, machine_options)
147
+ #
148
+ # What to do What to do.
149
+ #
150
+ # On one level there's really only one thing to do here,
151
+ # shellout and halt, or shutdown -h now,
152
+ # maybe provide abitily to pass some shutdown options
153
+ #
154
+ # But be vewwy vewwy careful:
155
+ #
156
+ # you better have console...
157
+ # or be close to your datacenter
158
+ #
159
+ end
160
+
161
+ def restart_machine(action_handler, machine_spec, machine_options)
162
+ # allocate_machine(action_handler, machine_spec, machine_options)
163
+ # Full Restart, POST BIOS and all
164
+ end
165
+
166
+ def reload_machine(action_handler, machine_spec, machine_options)
167
+ # allocate_machine(action_handler, machine_spec, machine_options)
168
+ # Use `kexec` here to skip POST and BIOS and all that noise.
169
+ end
170
+
171
+ # protected
172
+
173
+ def ensure_ssh_cluster(action_handler)
174
+ _cluster_path = cluster_path
175
+ Chef::Provisioning.inline_resource(action_handler) do
176
+ ssh_cluster _cluster_path
177
+ end
178
+ end
179
+
180
+ def existing_machine_hash(machine_spec)
181
+ raise("You Must Pass machine_spec unless existing_machine_hash exists") unless machine_spec
182
+ if ssh_machine_exists?(machine_spec.name)
183
+ existing_machine_hash = JSON.parse(File.read(existing_ssh_machine_file(machine_spec.name)))
184
+ existing_machine_hash
185
+ # else
186
+ # return false
187
+ # # raise('We have machine_spec.location but have no machine_spec.location["ssh_file_path"]. WTF?')
188
+ # end
189
+ else
190
+ return false
191
+ end
192
+ end
193
+
194
+ def delete_ssh_machine_file(action_handler, registry_file)
195
+ log_info("registry_file = #{registry_file}")
196
+ if ::File.exists?(registry_file)
197
+ Chef::Provisioning.inline_resource(action_handler) do
198
+ file registry_file do
199
+ action :delete
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ def ssh_machine_exists?(target_name)
206
+ ::File.exists?(existing_ssh_machine_file(target_name))
207
+ end
208
+
209
+ def existing_ssh_machine_file(target_name)
210
+ existing_ssh_machine_file = ::File.join(@cluster_path, "#{target_name}.json")
211
+ existing_ssh_machine_file
212
+ end
213
+
214
+ def create_ssh_machine_file(action_handler, target_name, use_machine_options)
215
+ log_info("File is = #{::File.join(@cluster_path, "#{target_name}.json")}")
216
+ log_info("current_machine_options = #{use_machine_options.to_s}")
217
+ stringify_keys(use_machine_options)
218
+ file_path = existing_ssh_machine_file(target_name)
219
+ options_parsed = ::JSON.parse(use_machine_options.to_json)
220
+ json_machine_options = ::JSON.pretty_generate(options_parsed)
221
+ Chef::Provisioning.inline_resource(action_handler) do
222
+ file file_path do
223
+ content json_machine_options
224
+ end
225
+ end
226
+ end
227
+
228
+ def updated_ssh_machine_file_hash(new_machine_options, current_machine_options)
229
+ log_info "updated_ssh_machine_file_hash --\nnew_machine_options = #{new_machine_options}\ncurrent_machine_options = #{current_machine_options}"
230
+ if new_machine_options && new_machine_options['convergence_options']
231
+ use_convergence_options = new_machine_options['convergence_options']
232
+ else
233
+ use_convergence_options = false
234
+ end
235
+
236
+ if new_machine_options && new_machine_options['transport_options']
237
+ new_transport_options = new_machine_options['transport_options']
238
+ else
239
+ false
240
+ end
241
+
242
+ if current_machine_options && current_machine_options['transport_options']
243
+ current_transport_options = current_machine_options['transport_options']
244
+ else
245
+ current_transport_options = new_transport_options
246
+ end
247
+ transport_options_hash = updated_transport_options_hash(new_transport_options,
248
+ current_transport_options)
249
+ new_hash = {}
250
+ new_hash['convergence_options'] = use_convergence_options if use_convergence_options
251
+ new_hash['transport_options'] = transport_options_hash
252
+ stringify_keys(new_hash)
253
+ return new_hash
254
+ end
255
+
256
+ def updated_transport_options_hash(new_transport_options, current_transport_options = false)
257
+ log_info "updated_transport_options_hash - new_transport_options if #{new_transport_options}"
258
+ current_transport_options = new_transport_options unless current_transport_options
259
+
260
+ new_ip = new_transport_options['ip_address'] rescue false
261
+ current_ip = current_transport_options['ip_address'] rescue false
262
+ if (new_ip && current_ip)
263
+ raise 'IP Addr Does not match' unless (new_ip == current_ip)
264
+ end
265
+ ip_address = (current_ip || new_ip)
266
+
267
+ new_hostname = new_transport_options['hostname'] rescue false
268
+ current_hostname = current_transport_options['hostname'] rescue false
269
+ hostname = (current_hostname || new_hostname)
270
+
271
+ new_host = new_transport_options['host'] rescue false
272
+ current_host = current_transport_options['host'] rescue false
273
+ given_host = (current_host || new_host)
274
+
275
+ raise 'We Gots No IP or Hostname. So, um, yeah.' unless (hostname ||
276
+ ip_address ||
277
+ given_host)
278
+
279
+ new_username = new_transport_options['username'] rescue false
280
+ new_ssh_options_user = new_transport_options['ssh_options']['user'] rescue false
281
+ current_username = current_transport_options['username'] rescue false
282
+ current_ssh_options_user = current_transport_options['ssh_options']['user'] rescue false
283
+ username = (current_username || current_ssh_options_user || new_username ||new_ssh_options_user)
284
+
285
+ opts = {}
286
+ opts['host'] = given_host if given_host
287
+ opts['hostname'] = hostname if hostname
288
+ opts['ip_address'] = ip_address if ip_address
289
+ host = host_for(opts)
290
+
291
+ current_transport_options ||= {}
292
+ current_transport_options['ssh_options'] ||= {}
293
+ current_transport_options['ssh_options']['keys'] = [] unless current_transport_options['ssh_options']['keys']
294
+ new_transport_options ||= {}
295
+ new_transport_options['ssh_options'] ||= {}
296
+ new_transport_options['ssh_options']['user'] = username
297
+ new_transport_options['ssh_options']['keys'] = [] unless new_transport_options['ssh_options']['keys']
298
+ new_keys = Array(current_transport_options['ssh_options']['keys']).concat( Array(new_transport_options['ssh_options']['keys']) ) || false
299
+ log_info("new_keys = #{new_keys}")
300
+
301
+ new_hash = {}
302
+ new_hash['host'] = host
303
+ new_hash['ip_address'] = ip_address if ip_address
304
+ new_hash['hostname'] = hostname if hostname
305
+ new_hash['ssh_options'] = {}
306
+ new_hash['ssh_options']['keys'] = new_keys if new_keys
307
+ if new_transport_options && new_transport_options['ssh_options']
308
+ new_hash['ssh_options']['password'] = new_transport_options['password'] if new_transport_options['password']
309
+ end
310
+
311
+ merged_transport_options = Chef::Mixin::DeepMerge.merge(current_transport_options, new_transport_options)
312
+ _merged_transport_options = merged_transport_options.dup
313
+ updated_transport_options_hash = _merged_transport_options.dup
314
+ updated_transport_options_hash = Chef::Mixin::DeepMerge.merge(merged_transport_options, new_hash) unless new_hash.empty?
315
+ ensure_has_keys_or_password(updated_transport_options_hash)
316
+ log_info "updated_transport_options_hash = #{updated_transport_options_hash}"
317
+ updated_transport_options_hash
318
+ end
319
+
320
+ def ensure_has_keys_or_password(transport_hash = false)
321
+ if transport_hash && transport_hash['ssh_options']
322
+ ssh_hash = transport_hash['ssh_options']
323
+ keys = ssh_hash['keys'] || false
324
+ password = ssh_hash['password'] || false
325
+ has_either = (password || (keys && !keys.empty?))
326
+ else
327
+ has_either = false
328
+ end
329
+ raise 'No Keys OR Password, No Can Do Compadre' unless has_either
330
+ return has_either ? true : false
331
+ end
332
+
333
+ def host_for(_transport_options)
334
+ @target_host = false
335
+ log_info "_transport_options #{_transport_options}"
336
+ transport_options = stringify_keys(_transport_options)
337
+
338
+ target_ip = transport_options['ip_address'] || false
339
+ target_fqdn = transport_options['hostname'] || false
340
+ target_host = transport_options['host'] || false
341
+
342
+ raise "no target_ip or target_fqdn given" unless(target_ip ||
343
+ target_host ||
344
+ target_fqdn )
345
+ if target_ip
346
+ raise 'Invalid IP' unless ( target_ip =~ Resolv::IPv4::Regex ||
347
+ target_ip =~ Resolv::IPv6::Regex )
348
+ @target_host = target_ip
349
+ elsif target_fqdn
350
+ rh = Resolv::Hosts.new
351
+ rd = Resolv.new
352
+ begin
353
+ rh.getaddress(target_fqdn)
354
+ in_hosts_file = true
355
+ rescue
356
+ in_hosts_file = false
357
+ end
358
+ begin
359
+ rd.getaddress(target_fqdn)
360
+ in_dns = true
361
+ rescue
362
+ in_dns = false
363
+ end
364
+ raise 'Unresolvable Hostname' unless (in_hosts_file || in_dns)
365
+ @target_host = target_fqdn
366
+ elsif target_host
367
+ @target_host = target_host
368
+ else
369
+ raise "aint got no target yo, that dog dont hunt"
370
+ end
371
+ log_debug("get_target_connection_method - @target_host: #{@target_host}")
372
+ @target_host
373
+ end
374
+
375
+ def can_connect?(_ssh_options, host)
376
+ end
377
+
378
+ def initialize_ssh(transport_options, options = {})
379
+ _transport_options_s = transport_options.dup
380
+ _transport_options = symbolize_keys(_transport_options_s.dup)
381
+ log_info "_transport_options is #{_transport_options}"
382
+ ssh_options = _transport_options[:ssh_options]
383
+ @host = _transport_options[:host] || false
384
+ @username = ssh_options[:user] rescue false
385
+ @ssh_options_for_transport = ssh_options_for(ssh_options)
386
+
387
+ new_options = options.empty? ? options : symbolize_keys(options)
388
+ new_options.merge!({:ssh_pty_enable => true}) unless new_options.has_key?(:ssh_pty_enable)
389
+ new_options.merge!({:prefix => 'sudo '}) unless @username == 'root'
390
+ @options = new_options
391
+ test = Chef::Provisioning::Transport::SSH.new(@host, @username, @ssh_options_for_transport, @options, config)
392
+ test.available?
393
+ end
394
+
395
+ def machine_options_for(given_machine_options = false)
396
+ if @machine_options_for
397
+ log_info "@machine_options_for #{@machine_options_for}"
398
+ return @machine_options_for
399
+ else
400
+ @machine_options_for ||= begin
401
+ _given_machine_options = given_machine_options.dup
402
+ ret_val = false
403
+ ret_val = symbolize_keys(_given_machine_options) if _given_machine_options
404
+ ret_val
405
+ end
406
+ end
407
+ end
408
+
409
+ def machine_for(machine_spec, machine_options)
410
+ Chef::Provisioning::Machine::UnixMachine.new(machine_spec,
411
+ transport_for(machine_options),
412
+ convergence_strategy_for(machine_spec, machine_options))
413
+ end
414
+
415
+ def transport_for(machine_options)
416
+ Chef::Provisioning::Transport::SSH.new(@target_host, @username, @ssh_options_for_transport, @options, config)
417
+ end
418
+
419
+ def convergence_strategy_for(machine_spec, machine_options)
420
+ @unix_convergence_strategy ||= begin
421
+ Chef::Provisioning::ConvergenceStrategy::InstallCached.new(machine_options[:convergence_options],
422
+ config)
423
+ end
424
+ end
425
+
426
+ # def create_ssh_transport(machine_options)
427
+ def ssh_options_for(given_ssh_options)
428
+ machine_ssh_options = stringify_keys(given_ssh_options)
429
+ # username = (@username || machine_ssh_options['user'] || ENV['METAL_SSH_USER'] || 'root')
430
+ log_info("machine_ssh_options: #{machine_ssh_options}")
431
+
432
+ ssh_pass = machine_ssh_options['password'] || false
433
+ ssh_pass_hash = { 'password' => ssh_pass } if ssh_pass
434
+
435
+ ssh_keys = []
436
+ if machine_ssh_options['keys']
437
+ Array(machine_ssh_options['keys']).each do |key|
438
+ ssh_keys << key
439
+ end
440
+ else
441
+ ssh_keys = false
442
+ end
443
+
444
+ ssh_key_hash = { 'keys' => ssh_keys.flatten.uniq } if ssh_keys
445
+
446
+ log_info("create_ssh_transport - ssh_pass: #{ssh_pass}") if ssh_pass
447
+ log_info("create_ssh_transport - ssh_keys: #{ssh_keys.inspect}") if ssh_keys
448
+ log_info("create_ssh_transport - no ssh_pass or ssh_key given") unless (ssh_keys || ssh_pass)
449
+ raise "no ssh_pass or ssh_key given" unless ( ssh_pass || ssh_keys )
450
+ machine_ssh_options.merge!(ssh_pass_hash) if ssh_pass_hash
451
+ machine_ssh_options.merge!(ssh_key_hash) if ssh_key_hash
452
+
453
+ # Validate Ssh Options
454
+ use_ssh_options = symbolize_keys(machine_ssh_options)
455
+ log_info "use_ssh_options #{use_ssh_options}"
456
+ use_ssh_options.each { |k,v| raise "Invalid Shh Option #{k} \n Valid Options are #{valid_ssh_options}" unless valid_ssh_options.include?(k) }
457
+
458
+ # Make Sure We Can Connect
459
+ log_debug("create_ssh_transport - ssh_options: #{use_ssh_options.inspect}")
460
+ begin
461
+ ssh = Net::SSH.start(@target_host, @username, use_ssh_options)
462
+ ssh.close
463
+ log_info("ABLE to Connect to #{@target_host} using #{@username} and #{use_ssh_options.inspect}")
464
+ rescue
465
+ log_info("UNABLE to Connect to #{@target_host} using #{@username} and #{use_ssh_options.inspect}")
466
+ raise "UNABLE to Connect to #{@target_host} using #{@username} and #{use_ssh_options.inspect}"
467
+ end
468
+
469
+ return use_ssh_options
470
+ end
471
+
472
+ end
473
+ end
474
+ end
475
+ end
@@ -0,0 +1,191 @@
1
+ class Chef
2
+ module Provisioning
3
+ module SshDriver
4
+ module Helpers
5
+
6
+ def log_info(str = false)
7
+ if str.kind_of?(String) && ENV['METAL_SSH_LOGGING_ENABLE']
8
+ if str.empty?
9
+ return
10
+ else
11
+ log_debug(str)
12
+ end
13
+ else
14
+ return
15
+ end
16
+ end
17
+
18
+ def log_ts(str)
19
+ put_val = []
20
+ put_val << ""
21
+ put_val << ("=================== BEGIN LOG ENTRY ====================>")
22
+ put_val << str
23
+ put_val << ("=================== END LOG ENTRY ====================>")
24
+ put_val << ""
25
+ puts put_val
26
+ end
27
+
28
+ def log_debug(str = false)
29
+ if str.kind_of?(String)
30
+ if str.empty?
31
+ return
32
+ else
33
+ if ENV['METAL_SSH_LOGGING_ENABLE']
34
+ put_val = []
35
+ put_val << ""
36
+ put_val << ("=================== BEGIN LOG ENTRY ====================>")
37
+ put_val << str
38
+ put_val << ("=================== END LOG ENTRY ====================>")
39
+ put_val << ""
40
+ puts put_val
41
+ else
42
+ Chef::Log.debug("======================================>")
43
+ Chef::Log.debug(str)
44
+ Chef::Log.debug("======================================>")
45
+ end
46
+ end
47
+ else
48
+ return
49
+ end
50
+ end
51
+
52
+ def symbolize_keys(hash)
53
+ if hash.is_a?(Hash)
54
+ hash.inject({}){|result, (key, value)|
55
+ new_key = case key
56
+ when String
57
+ key.to_sym
58
+ else
59
+ key
60
+ end
61
+
62
+ new_value = case value
63
+ when Hash
64
+ symbolize_keys(value)
65
+ when String
66
+ value
67
+ else
68
+ value
69
+ end
70
+
71
+ result[new_key] = new_value
72
+ result
73
+ }
74
+ end
75
+ end
76
+
77
+ def stringify_keys(hash)
78
+ if hash.is_a?(Hash)
79
+ hash.inject({}){|result, (key, value)|
80
+ new_key = case key
81
+ when Symbol
82
+ key.to_s
83
+ else
84
+ key
85
+ end
86
+
87
+ new_value = case value
88
+ when Hash
89
+ stringify_keys(value)
90
+ when String
91
+ value
92
+ else
93
+ value
94
+ end
95
+
96
+ result[new_key] = new_value
97
+ result
98
+ }
99
+ end
100
+ end
101
+
102
+ def false_or_value(v)
103
+ case v
104
+ when "false"
105
+ false
106
+ when false
107
+ false
108
+ else
109
+ v
110
+ end
111
+ end
112
+
113
+ def strip_hash_nil(val)
114
+ vvv = case val
115
+ when Hash
116
+ cleaned_val = val.delete_if { |kk,vv| vv.nil? || vv.empty? }
117
+ cleaned_val.each do |k,v|
118
+ case v
119
+ when Hash
120
+ strip_hash_nil(v)
121
+ when Array
122
+ v.flatten!
123
+ v.uniq!
124
+ end
125
+ end
126
+ end
127
+ # puts "VVV is #{vvv}"
128
+ vvv
129
+ end
130
+
131
+ def valid_ip?(given_ip)
132
+ if ip_is_valid?(given_ip)
133
+ true
134
+ else
135
+ false
136
+ end
137
+ end
138
+
139
+ def ip_is_valid?(given_ip)
140
+ valid_ip = ( given_ip =~ Resolv::IPv4::Regex || given_ip =~ Resolv::IPv6::Regex )
141
+ valid_ip
142
+ end
143
+
144
+ def valid_ssh_options
145
+ vso = [
146
+ :auth_methods,
147
+ :bind_address,
148
+ :compression,
149
+ :compression_level,
150
+ :config,
151
+ :encryption,
152
+ :forward_agent,
153
+ :hmac,
154
+ :host_key,
155
+ :keepalive,
156
+ :keepalive_interval,
157
+ :kex,
158
+ :keys,
159
+ :key_data,
160
+ :languages,
161
+ :logger,
162
+ :password,
163
+ :paranoid,
164
+ :port,
165
+ :proxy,
166
+ :rekey_blocks_limit,
167
+ :rekey_limit,
168
+ :rekey_packet_limit,
169
+ :timeout,
170
+ :verbose,
171
+ :global_known_hosts_file,
172
+ :user_known_hosts_file,
173
+ :host_key_alias,
174
+ :host_name,
175
+ :user,
176
+ :properties,
177
+ :passphrase,
178
+ :keys_only,
179
+ :max_pkt_size,
180
+ :max_win_size,
181
+ :send_env,
182
+ :use_agent
183
+ ]
184
+ vso
185
+ end
186
+
187
+ end
188
+ end
189
+ end
190
+ end
191
+
@@ -0,0 +1,7 @@
1
+ class Chef
2
+ module Provisioning
3
+ module SshDriver
4
+ VERSION = '0.0.3'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,21 @@
1
+ require 'chef/resource/lwrp_base'
2
+ require 'chef/provisioning/ssh_driver'
3
+
4
+ class Chef::Resource::SshCluster < Chef::Resource::LWRPBase
5
+ self.resource_name = 'ssh_cluster'
6
+
7
+ actions :create, :delete, :nothing
8
+ default_action :create
9
+
10
+ attribute :path, :kind_of => String, :name_attribute => true
11
+
12
+ def after_created
13
+ super
14
+ run_context.chef_provisioning.with_driver "ssh:#{path}"
15
+ end
16
+
17
+ # We are not interested in Chef's cloning behavior here.
18
+ def load_prior_resource
19
+ Chef::Log.debug("Overloading #{resource_name}.load_prior_resource with NOOP")
20
+ end
21
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chef-provisioning-ssh
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Zack Zondlo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: chef-provisioning
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Provisioner for managing servers using ssh in Chef Provisioning.
70
+ email: zackzondlo@gmail.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files:
74
+ - README.md
75
+ - LICENSE
76
+ files:
77
+ - LICENSE
78
+ - README.md
79
+ - Rakefile
80
+ - lib/chef/provider/ssh_cluster.rb
81
+ - lib/chef/provisioning/driver_init/ssh.rb
82
+ - lib/chef/provisioning/ssh_driver.rb
83
+ - lib/chef/provisioning/ssh_driver/driver.rb
84
+ - lib/chef/provisioning/ssh_driver/helpers.rb
85
+ - lib/chef/provisioning/ssh_driver/version.rb
86
+ - lib/chef/resource/ssh_cluster.rb
87
+ homepage: https://github.com/double-z/chef-provisioning-ssh
88
+ licenses: []
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 2.4.1
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: Provisioner for managing servers using ssh in Chef Provisioning.
110
+ test_files: []
111
+ has_rdoc: