knife-undev 0.0.8 → 0.0.9

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