chef-provisioning-ssh 0.0.3

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 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: