knife-undev 0.0.8 → 0.0.9

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: 0b10ee6fac2453b117a90410553a6da337329ab7
4
- data.tar.gz: b966038f848f734a1e938a824b8fd4947c3ac759
3
+ metadata.gz: 2ba84740385210e9d5e64a67a37f43919da6539f
4
+ data.tar.gz: 527e5ae42dbe266d9903d749c6c4189d214d5b28
5
5
  SHA512:
6
- metadata.gz: 73b67c4030e75b383c757014b88ca32954dabbc7ded496ecd1cb6e05f455d775c8c209e4949787b0de7b0f58ad2247af612412877f8a1f9ba8d0350f1245a296
7
- data.tar.gz: ced24f8cad9ec235a9c97d8d6aa6ea36090c893bfa64cc69598e078730486dfe7d066161da995b8839bdaf97e992f6cb3b24c7d6b93ed75a1d453ed46f57dfd9
6
+ metadata.gz: 78e2afbe003643a7906208f7d3c8ebe4472cfa5522b1d66a5b090eae68b439903edf34ebc35367ce43c55bd08b8f2f3ad7567e95a55540db3bd8e90790b64dc1
7
+ data.tar.gz: 44fd9445f1badce11fb05991c3d5747eca43c726bba015a2a88c1eb666df8d8b7fdf5ecbead9a643890325247f9b0f51ec5b9fe7e9439d4ad515794d9f711bab
data/lib/knife/undev.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  require "knife/undev/version"
2
2
  require "knife/undev/monkey_patches/object_loader"
3
3
  require "knife/undev/monkey_patches/node_presenter"
4
- require "knife/undev/monkey_patches/ssh"
4
+ require "knife/undev/plugins/cloud_ssh"
@@ -1,17 +1,14 @@
1
- require 'chef/knife'
2
1
  require 'chef/knife/core/node_presenter'
3
2
 
4
- class Chef
5
- class Knife
6
- module Core
7
3
 
8
- def summarize(data)
9
- if data.kind_of?(Chef::Node)
10
- node = data
11
- # special case ec2 with their split horizon whatsis.
12
- ip = (node[:private_ipaddress]) || node[:ipaddress]
4
+ class Chef::Knife::Core::NodePresenter
13
5
 
14
- summarized=<<-SUMMARY
6
+ def summarize(data)
7
+ if data.kind_of?(Chef::Node)
8
+ node = data
9
+ ip = node[:ipaddress]
10
+
11
+ summarized=<<-SUMMARY
15
12
  #{ui.color('Node Name:', :bold)} #{ui.color(node.name, :bold)}
16
13
  #{key('Environment:')} #{node.chef_environment}
17
14
  #{key('FQDN:')} #{node[:fqdn]}
@@ -23,14 +20,15 @@ class Chef
23
20
  #{key('Platform:')} #{node[:platform]} #{node[:platform_version]}
24
21
  #{key('Tags:')} #{Array(node[:tags]).join(', ')}
25
22
  SUMMARY
26
- if config[:medium_output] || config[:long_output]
27
- summarized +=<<-MORE
23
+ if config[:medium_output] || config[:long_output]
24
+ summarized +=<<-MORE
28
25
  #{key('Attributes:')}
29
26
  #{text_format(node.normal_attrs)}
30
27
  MORE
31
- end
32
- if config[:long_output]
33
- summarized +=<<-MOST
28
+ end
29
+
30
+ if config[:long_output]
31
+ summarized +=<<-MOST
34
32
  #{key('Default Attributes:')}
35
33
  #{text_format(node.default_attrs)}
36
34
  #{key('Override Attributes:')}
@@ -38,13 +36,12 @@ MORE
38
36
  #{key('Automatic Attributes (Ohai Data):')}
39
37
  #{text_format(node.automatic_attrs)}
40
38
  MOST
41
- end
42
- summarized
43
- else
44
- super
45
- end
46
- end
47
-
39
+ end
40
+
41
+ summarized
42
+ else # not Chef::Node
43
+ super
48
44
  end
49
45
  end
46
+
50
47
  end
@@ -0,0 +1,325 @@
1
+ require 'chef/knife'
2
+
3
+ # отличается от ssh:
4
+ # 1. host_key_verify => false
5
+ # 2. concurrency => 50
6
+ # 3. коннектиться по дефолту к private_ip
7
+ # 4. выдает exit status не нулевой если есть сфеленные по коннекту
8
+ # 5. выводит список всех сфейленных машин в самом конце
9
+
10
+ class Chef
11
+ class Knife
12
+ class CloudSsh < Knife
13
+
14
+ deps do
15
+ require 'net/ssh'
16
+ require 'net/ssh/multi'
17
+ require 'chef/monkey_patches/net-ssh-multi'
18
+ require 'readline'
19
+ require 'chef/exceptions'
20
+ require 'chef/search/query'
21
+ require 'chef/mixin/shell_out'
22
+ require 'mixlib/shellout'
23
+ end
24
+
25
+ include Chef::Mixin::ShellOut
26
+
27
+ attr_writer :password
28
+
29
+ banner "knife private_ssh QUERY COMMAND (options)"
30
+
31
+ option :concurrency,
32
+ :short => "-C NUM",
33
+ :long => "--concurrency NUM",
34
+ :description => "The number of concurrent connections",
35
+ :default => 50,
36
+ :proc => lambda { |o| o.to_i }
37
+
38
+ option :attribute,
39
+ :short => "-a ATTR",
40
+ :long => "--attribute ATTR",
41
+ :description => "The attribute to use for opening the connection - default depends on the context",
42
+ :proc => Proc.new { |key| Chef::Config[:knife][:ssh_attribute] = key.strip }
43
+
44
+ option :manual,
45
+ :short => "-m",
46
+ :long => "--manual-list",
47
+ :boolean => true,
48
+ :description => "QUERY is a space separated list of servers",
49
+ :default => false
50
+
51
+ option :ssh_user,
52
+ :short => "-x USERNAME",
53
+ :long => "--ssh-user USERNAME",
54
+ :description => "The ssh username"
55
+
56
+ option :ssh_password,
57
+ :short => "-P PASSWORD",
58
+ :long => "--ssh-password PASSWORD",
59
+ :description => "The ssh password"
60
+
61
+ option :ssh_port,
62
+ :short => "-p PORT",
63
+ :long => "--ssh-port PORT",
64
+ :description => "The ssh port",
65
+ :proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key.strip }
66
+
67
+ option :ssh_gateway,
68
+ :short => "-G GATEWAY",
69
+ :long => "--ssh-gateway GATEWAY",
70
+ :description => "The ssh gateway",
71
+ :proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key.strip }
72
+
73
+ option :forward_agent,
74
+ :short => "-A",
75
+ :long => "--forward-agent",
76
+ :description => "Enable SSH agent forwarding",
77
+ :boolean => true
78
+
79
+ option :identity_file,
80
+ :short => "-i IDENTITY_FILE",
81
+ :long => "--identity-file IDENTITY_FILE",
82
+ :description => "The SSH identity file used for authentication"
83
+
84
+ option :host_key_verify,
85
+ :long => "--[no-]host-key-verify",
86
+ :description => "Verify host key, disabled by default.",
87
+ :boolean => true,
88
+ :default => false
89
+
90
+ def session
91
+ config[:on_error] ||= :skip
92
+ ssh_error_handler = Proc.new do |server|
93
+ @failed_connect_nodes << { "node" => server.host , "message" => "#{$!.class.name}: #{$!.message}" }
94
+ if config[:manual]
95
+ node_name = server.host
96
+ else
97
+ @action_nodes.each do |n|
98
+ node_name = n if format_for_display(n)[config[:attribute]] == server.host
99
+ end
100
+ end
101
+ case config[:on_error]
102
+ when :skip
103
+ ui.warn "Failed to connect to #{node_name} -- #{$!.class.name}: #{$!.message}"
104
+ $!.backtrace.each { |l| Chef::Log.debug(l) }
105
+ when :raise
106
+ #Net::SSH::Multi magic to force exception to be re-raised.
107
+ throw :go, :raise
108
+ end
109
+ end
110
+
111
+ @session ||= Net::SSH::Multi.start(:concurrent_connections => config[:concurrency], :on_error => ssh_error_handler)
112
+ end
113
+
114
+ def configure_gateway
115
+ config[:ssh_gateway] ||= Chef::Config[:knife][:ssh_gateway]
116
+ if config[:ssh_gateway]
117
+ gw_host, gw_user = config[:ssh_gateway].split('@').reverse
118
+ gw_host, gw_port = gw_host.split(':')
119
+ gw_opts = gw_port ? { :port => gw_port } : {}
120
+
121
+ session.via(gw_host, gw_user || config[:ssh_user], gw_opts)
122
+ end
123
+ rescue Net::SSH::AuthenticationFailed
124
+ user = gw_user || config[:ssh_user]
125
+ prompt = "Enter the password for #{user}@#{gw_host}: "
126
+ gw_opts.merge!(:password => prompt_for_password(prompt))
127
+ session.via(gw_host, user, gw_opts)
128
+ end
129
+
130
+ def configure_session
131
+ list = case config[:manual]
132
+ when true
133
+ @name_args[0].split(" ")
134
+ when false
135
+ r = Array.new
136
+ q = Chef::Search::Query.new
137
+ @action_nodes = q.search(:node, @name_args[0])[0]
138
+ @action_nodes.each do |item|
139
+ # we should skip the loop to next iteration if the item returned by the search is nil
140
+ next if item.nil?
141
+ # if a command line attribute was not passed, and we have a cloud public_hostname, use that.
142
+ # see #configure_attribute for the source of config[:attribute] and config[:override_attribute]
143
+ if !config[:override_attribute] && item[:cloud] and item[:cloud][:public_hostname]
144
+ i = item[:cloud][:public_hostname]
145
+ elsif config[:override_attribute]
146
+ i = extract_nested_value(item, config[:override_attribute])
147
+ else
148
+ i = extract_nested_value(item, config[:attribute])
149
+ end
150
+ # next if we couldn't find the specified attribute in the returned node object
151
+ next if i.nil?
152
+ r.push(i)
153
+ end
154
+ r
155
+ end
156
+ if list.length == 0
157
+ if @action_nodes.length == 0
158
+ ui.fatal("No nodes returned from search!")
159
+ else
160
+ ui.fatal("#{@action_nodes.length} #{@action_nodes.length > 1 ? "nodes":"node"} found, " +
161
+ "but does not have the required attribute to establish the connection. " +
162
+ "Try setting another attribute to open the connection using --attribute.")
163
+ end
164
+ exit 10
165
+ end
166
+ session_from_list(list)
167
+ end
168
+
169
+ def session_from_list(list)
170
+ list.each do |item|
171
+ Chef::Log.debug("Adding #{item}")
172
+ session_opts = {}
173
+
174
+ ssh_config = Net::SSH.configuration_for(item)
175
+
176
+ # Chef::Config[:knife][:ssh_user] is parsed in #configure_user and written to config[:ssh_user]
177
+ user = config[:ssh_user] || ssh_config[:user]
178
+ hostspec = user ? "#{user}@#{item}" : item
179
+ session_opts[:keys] = File.expand_path(config[:identity_file]) if config[:identity_file]
180
+ session_opts[:keys_only] = true if config[:identity_file]
181
+ session_opts[:password] = config[:ssh_password] if config[:ssh_password]
182
+ session_opts[:forward_agent] = config[:forward_agent]
183
+ session_opts[:port] = config[:ssh_port] || Chef::Config[:knife][:ssh_port] || ssh_config[:port]
184
+ session_opts[:logger] = Chef::Log.logger if Chef::Log.level == :debug
185
+
186
+ if !config[:host_key_verify]
187
+ session_opts[:paranoid] = false
188
+ session_opts[:user_known_hosts_file] = "/dev/null"
189
+ end
190
+
191
+ session.use(hostspec, session_opts)
192
+
193
+ @longest = item.length if item.length > @longest
194
+ end
195
+
196
+ session
197
+ end
198
+
199
+ def fixup_sudo(command)
200
+ command.sub(/^sudo/, 'sudo -p \'knife sudo password: \'')
201
+ end
202
+
203
+ def print_data(host, data)
204
+ @buffers ||= {}
205
+ if leftover = @buffers[host]
206
+ @buffers[host] = nil
207
+ print_data(host, leftover + data)
208
+ else
209
+ if newline_index = data.index("\n")
210
+ line = data.slice!(0...newline_index)
211
+ data.slice!(0)
212
+ print_line(host, line)
213
+ print_data(host, data)
214
+ else
215
+ @buffers[host] = data
216
+ end
217
+ end
218
+ end
219
+
220
+ def print_line(host, data)
221
+ padding = @longest - host.length
222
+ str = ui.color(host, :cyan) + (" " * (padding + 1)) + data
223
+ ui.msg(str)
224
+ end
225
+
226
+ def ssh_command(command, subsession=nil)
227
+ exit_status = 0
228
+ subsession ||= session
229
+ command = fixup_sudo(command)
230
+ command.force_encoding('binary') if command.respond_to?(:force_encoding)
231
+ subsession.open_channel do |ch|
232
+ ch.request_pty
233
+ ch.exec command do |ch, success|
234
+ raise ArgumentError, "Cannot execute #{command}" unless success
235
+ ch.on_data do |ichannel, data|
236
+ print_data(ichannel[:host], data)
237
+ if data =~ /^knife sudo password: /
238
+ print_data(ichannel[:host], "\n")
239
+ ichannel.send_data("#{get_password}\n")
240
+ end
241
+ end
242
+ ch.on_request "exit-status" do |ichannel, data|
243
+ exit_status = [exit_status, data.read_long].max
244
+ @not_zerro_nodes << { 'node' => ichannel[:host], "message" => "exit status: #{exit_status}" } if exit_status != 0
245
+ end
246
+ end
247
+ end
248
+ session.loop
249
+ exit_status
250
+ end
251
+
252
+ def get_password
253
+ @password ||= prompt_for_password
254
+ end
255
+
256
+ def prompt_for_password(prompt = "Enter your password: ")
257
+ ui.ask(prompt) { |q| q.echo = false }
258
+ end
259
+
260
+ def configure_attribute
261
+ config[:override_attribute] = config[:attribute] || Chef::Config[:knife][:ssh_attribute]
262
+ config[:attribute] = (Chef::Config[:knife][:ssh_attribute] ||
263
+ config[:attribute] ||
264
+ "private_ipaddress").strip
265
+ end
266
+
267
+
268
+ def get_stripped_unfrozen_value(value)
269
+ return nil if value.nil?
270
+ value.strip
271
+ end
272
+
273
+ def configure_user
274
+ config[:ssh_user] = get_stripped_unfrozen_value(config[:ssh_user] ||
275
+ Chef::Config[:knife][:ssh_user])
276
+ end
277
+
278
+ def configure_identity_file
279
+ config[:identity_file] = get_stripped_unfrozen_value(config[:identity_file] ||
280
+ Chef::Config[:knife][:ssh_identity_file])
281
+ end
282
+
283
+ def extract_nested_value(data_structure, path_spec)
284
+ ui.presenter.extract_nested_value(data_structure, path_spec)
285
+ end
286
+
287
+ def print_failed(arr = [{"node" => "ip", "meesage" => "message"}])
288
+ return if arr.compact.empty?
289
+ arr.each do |status|
290
+ ui.error("#{status['node']} #{status['message']}")
291
+ end
292
+ end
293
+
294
+ def run
295
+ extend Chef::Mixin::Command
296
+
297
+ @longest = 0
298
+
299
+ @failed_connect_nodes = []
300
+ @not_zerro_nodes = []
301
+
302
+ configure_attribute
303
+ configure_user
304
+ configure_identity_file
305
+ configure_gateway
306
+ configure_session
307
+
308
+ exit_status = ssh_command(@name_args[1..-1].join(" "))
309
+
310
+ session.close
311
+
312
+ print_failed(@failed_connect_nodes)
313
+ print_failed(@not_zerro_nodes)
314
+
315
+ if (@not_zerro_nodes + @failed_connect_nodes).compact.empty?
316
+ exit_status
317
+ else
318
+ my_status = exit_status == 0 ? 1 : exit_status
319
+ exit my_status
320
+ end
321
+ end
322
+
323
+ end
324
+ end
325
+ end
@@ -1,5 +1,5 @@
1
1
  module Knife
2
2
  module Undev
3
- VERSION = "0.0.8"
3
+ VERSION = "0.0.9"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife-undev
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vasiliev Dmitry
@@ -101,7 +101,7 @@ files:
101
101
  - lib/knife/undev/converter.rb
102
102
  - lib/knife/undev/monkey_patches/node_presenter.rb
103
103
  - lib/knife/undev/monkey_patches/object_loader.rb
104
- - lib/knife/undev/monkey_patches/ssh.rb
104
+ - lib/knife/undev/plugins/cloud_ssh.rb
105
105
  - lib/knife/undev/version.rb
106
106
  homepage: ''
107
107
  licenses:
@@ -1,17 +0,0 @@
1
- require 'chef/knife'
2
- require 'chef/knife/ssh'
3
-
4
- class Chef
5
- class Knife
6
- class Ssh
7
-
8
- def configure_attribute
9
- config[:override_attribute] = config[:attribute] || Chef::Config[:knife][:ssh_attribute]
10
- config[:attribute] = (Chef::Config[:knife][:ssh_attribute] ||
11
- config[:attribute] || "private_ipaddress").strip
12
- end
13
-
14
- end
15
- end
16
- end
17
-