knife-windows 1.0.0.rc.1 → 1.0.0.rc.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -5
  3. data/.travis.yml +20 -20
  4. data/CHANGELOG.md +75 -74
  5. data/DOC_CHANGES.md +323 -323
  6. data/Gemfile +12 -12
  7. data/LICENSE +201 -201
  8. data/README.md +393 -292
  9. data/RELEASE_NOTES.md +79 -74
  10. data/Rakefile +21 -16
  11. data/appveyor.yml +42 -42
  12. data/ci.gemfile +15 -15
  13. data/features/knife_help.feature +20 -20
  14. data/features/support/env.rb +5 -5
  15. data/knife-windows.gemspec +28 -28
  16. data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +247 -241
  17. data/lib/chef/knife/bootstrap_windows_base.rb +388 -368
  18. data/lib/chef/knife/bootstrap_windows_ssh.rb +110 -110
  19. data/lib/chef/knife/bootstrap_windows_winrm.rb +102 -113
  20. data/lib/chef/knife/core/windows_bootstrap_context.rb +361 -362
  21. data/lib/chef/knife/knife_windows_base.rb +33 -0
  22. data/lib/chef/knife/windows_cert_generate.rb +155 -155
  23. data/lib/chef/knife/windows_cert_install.rb +68 -68
  24. data/lib/chef/knife/windows_helper.rb +36 -36
  25. data/lib/chef/knife/windows_listener_create.rb +107 -107
  26. data/lib/chef/knife/winrm.rb +212 -191
  27. data/lib/chef/knife/winrm_base.rb +118 -125
  28. data/lib/chef/knife/winrm_knife_base.rb +218 -201
  29. data/lib/chef/knife/winrm_session.rb +80 -71
  30. data/lib/chef/knife/winrm_shared_options.rb +47 -47
  31. data/lib/chef/knife/wsman_endpoint.rb +44 -44
  32. data/lib/chef/knife/wsman_test.rb +96 -96
  33. data/lib/knife-windows/path_helper.rb +234 -234
  34. data/lib/knife-windows/version.rb +6 -6
  35. data/spec/assets/win_template_rendered_with_bootstrap_install_command.txt +217 -0
  36. data/spec/assets/win_template_rendered_without_bootstrap_install_command.txt +329 -0
  37. data/spec/assets/win_template_unrendered.txt +246 -0
  38. data/spec/functional/bootstrap_download_spec.rb +216 -140
  39. data/spec/spec_helper.rb +87 -72
  40. data/spec/unit/knife/bootstrap_options_spec.rb +146 -146
  41. data/spec/unit/knife/bootstrap_template_spec.rb +92 -92
  42. data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +240 -161
  43. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +151 -101
  44. data/spec/unit/knife/windows_cert_generate_spec.rb +90 -90
  45. data/spec/unit/knife/windows_cert_install_spec.rb +51 -51
  46. data/spec/unit/knife/windows_listener_create_spec.rb +76 -76
  47. data/spec/unit/knife/winrm_session_spec.rb +55 -46
  48. data/spec/unit/knife/winrm_spec.rb +504 -376
  49. data/spec/unit/knife/wsman_test_spec.rb +175 -175
  50. metadata +28 -8
@@ -1,36 +1,36 @@
1
- #
2
- # Author:: Chirag Jog (<chirag@clogeny.com>)
3
- # Copyright:: Copyright (c) 2013 Opscode, Inc.
4
- # License:: Apache License, Version 2.0
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
- #
18
-
19
- require 'chef/knife'
20
- require 'chef/knife/winrm'
21
- require 'chef/knife/bootstrap_windows_ssh'
22
- require 'chef/knife/bootstrap_windows_winrm'
23
- require 'chef/knife/wsman_test'
24
-
25
- class Chef
26
- class Knife
27
- class WindowsHelper < Knife
28
-
29
- banner "#{BootstrapWindowsWinrm.banner}\n" +
30
- "#{BootstrapWindowsSsh.banner}\n" +
31
- "#{Winrm.banner}\n" +
32
- "#{WsmanTest.banner}"
33
- end
34
- end
35
- end
36
-
1
+ #
2
+ # Author:: Chirag Jog (<chirag@clogeny.com>)
3
+ # Copyright:: Copyright (c) 2013 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/knife'
20
+ require 'chef/knife/winrm'
21
+ require 'chef/knife/bootstrap_windows_ssh'
22
+ require 'chef/knife/bootstrap_windows_winrm'
23
+ require 'chef/knife/wsman_test'
24
+
25
+ class Chef
26
+ class Knife
27
+ class WindowsHelper < Knife
28
+
29
+ banner "#{BootstrapWindowsWinrm.banner}\n" +
30
+ "#{BootstrapWindowsSsh.banner}\n" +
31
+ "#{Winrm.banner}\n" +
32
+ "#{WsmanTest.banner}"
33
+ end
34
+ end
35
+ end
36
+
@@ -1,107 +1,107 @@
1
- # Author:: Mukta Aphale (<mukta.aphale@clogeny.com>)
2
- # Copyright:: Copyright (c) 2014 Opscode, Inc.
3
- # License:: Apache License, Version 2.0
4
- #
5
- # Licensed under the Apache License, Version 2.0 (the "License");
6
- # you may not use this file except in compliance with the License.
7
- # You may obtain a copy of the License at
8
- #
9
- # http://www.apache.org/licenses/LICENSE-2.0
10
- #
11
- # Unless required by applicable law or agreed to in writing, software
12
- # distributed under the License is distributed on an "AS IS" BASIS,
13
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- # See the License for the specific language governing permissions and
15
- # limitations under the License.
16
- #
17
-
18
- require 'chef/knife'
19
- require 'chef/knife/winrm_base'
20
- require 'openssl'
21
-
22
- class Chef
23
- class Knife
24
- class WindowsListenerCreate < Knife
25
-
26
- banner "knife windows listener create (options)"
27
-
28
- option :cert_install,
29
- :short => "-c CERT_PATH",
30
- :long => "--cert-install CERT_PATH",
31
- :description => "Adds specified certificate to the Windows Certificate Store's Local Machine personal store before creating listener."
32
-
33
- option :port,
34
- :short => "-p PORT",
35
- :long => "--port PORT",
36
- :description => "Specify port. Default is 5986",
37
- :default => "5986"
38
-
39
- option :hostname,
40
- :short => "-h HOSTNAME",
41
- :long => "--hostname HOSTNAME",
42
- :description => "Hostname on the listener. Default is blank",
43
- :default => ""
44
-
45
- option :cert_thumbprint,
46
- :short => "-t THUMBPRINT",
47
- :long => "--cert-thumbprint THUMBPRINT",
48
- :description => "Thumbprint of the certificate. Required only if --cert-install option is not used."
49
-
50
- option :cert_passphrase,
51
- :short => "-cp PASSWORD",
52
- :long => "--cert-passphrase PASSWORD",
53
- :description => "Password for certificate."
54
-
55
- def get_cert_passphrase
56
- print "Enter given certificate's passphrase (empty for no passphrase):"
57
- passphrase = STDIN.gets
58
- passphrase.strip
59
- end
60
-
61
- def run
62
- STDOUT.sync = STDERR.sync = true
63
-
64
- if Chef::Platform.windows?
65
- begin
66
- if config[:cert_install]
67
- config[:cert_passphrase] = get_cert_passphrase unless config[:cert_passphrase]
68
- result = %x{powershell.exe -Command " '#{config[:cert_passphrase]}' | certutil -importPFX '#{config[:cert_install]}' AT_KEYEXCHANGE"}
69
- if $?.exitstatus
70
- ui.info "Certificate installed to Certificate Store"
71
- result = %x{powershell.exe -Command " echo (Get-PfxCertificate #{config[:cert_install]}).thumbprint "}
72
- ui.info "Certificate Thumbprint: #{result}"
73
- config[:cert_thumbprint] = result.strip
74
- else
75
- ui.error "Error installing certificate to Certificate Store"
76
- ui.error result
77
- exit 1
78
- end
79
- end
80
-
81
- unless config[:cert_thumbprint]
82
- ui.error "Please specify the --cert-thumbprint"
83
- exit 1
84
- end
85
-
86
- result = %x{winrm create winrm/config/Listener?Address=*+Transport=HTTPS @{Hostname="#{config[:hostname]}";CertificateThumbprint="#{config[:cert_thumbprint]}";Port="#{config[:port]}"}}
87
- Chef::Log.debug result
88
-
89
- if ($?.exitstatus == 0)
90
- ui.info "WinRM listener created with Port: #{config[:port]} and CertificateThumbprint: #{config[:cert_thumbprint]}"
91
- else
92
- ui.error "Error creating WinRM listener. use -VV for more details."
93
- exit 1
94
- end
95
-
96
- rescue => e
97
- puts "ERROR: + #{e}"
98
- end
99
- else
100
- ui.error "WinRM listener can be created on Windows system only"
101
- exit 1
102
- end
103
- end
104
-
105
- end
106
- end
107
- end
1
+ # Author:: Mukta Aphale (<mukta.aphale@clogeny.com>)
2
+ # Copyright:: Copyright (c) 2014 Opscode, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'chef/knife'
19
+ require 'chef/knife/winrm_base'
20
+ require 'openssl'
21
+
22
+ class Chef
23
+ class Knife
24
+ class WindowsListenerCreate < Knife
25
+
26
+ banner "knife windows listener create (options)"
27
+
28
+ option :cert_install,
29
+ :short => "-c CERT_PATH",
30
+ :long => "--cert-install CERT_PATH",
31
+ :description => "Adds specified certificate to the Windows Certificate Store's Local Machine personal store before creating listener."
32
+
33
+ option :port,
34
+ :short => "-p PORT",
35
+ :long => "--port PORT",
36
+ :description => "Specify port. Default is 5986",
37
+ :default => "5986"
38
+
39
+ option :hostname,
40
+ :short => "-h HOSTNAME",
41
+ :long => "--hostname HOSTNAME",
42
+ :description => "Hostname on the listener. Default is blank",
43
+ :default => ""
44
+
45
+ option :cert_thumbprint,
46
+ :short => "-t THUMBPRINT",
47
+ :long => "--cert-thumbprint THUMBPRINT",
48
+ :description => "Thumbprint of the certificate. Required only if --cert-install option is not used."
49
+
50
+ option :cert_passphrase,
51
+ :short => "-cp PASSWORD",
52
+ :long => "--cert-passphrase PASSWORD",
53
+ :description => "Password for certificate."
54
+
55
+ def get_cert_passphrase
56
+ print "Enter given certificate's passphrase (empty for no passphrase):"
57
+ passphrase = STDIN.gets
58
+ passphrase.strip
59
+ end
60
+
61
+ def run
62
+ STDOUT.sync = STDERR.sync = true
63
+
64
+ if Chef::Platform.windows?
65
+ begin
66
+ if config[:cert_install]
67
+ config[:cert_passphrase] = get_cert_passphrase unless config[:cert_passphrase]
68
+ result = %x{powershell.exe -Command " '#{config[:cert_passphrase]}' | certutil -importPFX '#{config[:cert_install]}' AT_KEYEXCHANGE"}
69
+ if $?.exitstatus
70
+ ui.info "Certificate installed to Certificate Store"
71
+ result = %x{powershell.exe -Command " echo (Get-PfxCertificate #{config[:cert_install]}).thumbprint "}
72
+ ui.info "Certificate Thumbprint: #{result}"
73
+ config[:cert_thumbprint] = result.strip
74
+ else
75
+ ui.error "Error installing certificate to Certificate Store"
76
+ ui.error result
77
+ exit 1
78
+ end
79
+ end
80
+
81
+ unless config[:cert_thumbprint]
82
+ ui.error "Please specify the --cert-thumbprint"
83
+ exit 1
84
+ end
85
+
86
+ result = %x{winrm create winrm/config/Listener?Address=*+Transport=HTTPS @{Hostname="#{config[:hostname]}";CertificateThumbprint="#{config[:cert_thumbprint]}";Port="#{config[:port]}"}}
87
+ Chef::Log.debug result
88
+
89
+ if ($?.exitstatus == 0)
90
+ ui.info "WinRM listener created with Port: #{config[:port]} and CertificateThumbprint: #{config[:cert_thumbprint]}"
91
+ else
92
+ ui.error "Error creating WinRM listener. use -VV for more details."
93
+ exit 1
94
+ end
95
+
96
+ rescue => e
97
+ puts "ERROR: + #{e}"
98
+ end
99
+ else
100
+ ui.error "WinRM listener can be created on Windows system only"
101
+ exit 1
102
+ end
103
+ end
104
+
105
+ end
106
+ end
107
+ end
@@ -1,191 +1,212 @@
1
- #
2
- # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
- # Copyright:: Copyright (c) 2011 Opscode, Inc.
4
- # License:: Apache License, Version 2.0
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
- #
18
-
19
- require 'chef/knife'
20
- require 'chef/knife/winrm_knife_base'
21
- require 'chef/knife/windows_cert_generate'
22
- require 'chef/knife/windows_cert_install'
23
- require 'chef/knife/windows_listener_create'
24
- require 'chef/knife/winrm_session'
25
-
26
- class Chef
27
- class Knife
28
- class Winrm < Knife
29
-
30
- include Chef::Knife::WinrmCommandSharedFunctions
31
-
32
- deps do
33
- require 'readline'
34
- require 'chef/search/query'
35
- end
36
-
37
- attr_writer :password
38
-
39
- banner "knife winrm QUERY COMMAND (options)"
40
-
41
- option :returns,
42
- :long => "--returns CODES",
43
- :description => "A comma delimited list of return codes which indicate success",
44
- :default => "0"
45
-
46
- def run
47
- STDOUT.sync = STDERR.sync = true
48
-
49
- configure_session
50
- validate_password
51
- execute_remote_command
52
- end
53
-
54
- def execute_remote_command
55
- begin
56
- case @name_args[1]
57
- when "interactive"
58
- interactive
59
- else
60
- relay_winrm_command(@name_args[1..-1].join(" "))
61
-
62
- if config[:returns]
63
- check_for_errors!
64
- end
65
-
66
- # Knife seems to ignore the return value of this method,
67
- # so we exit to force the process exit code for this
68
- # subcommand if returns is set
69
- exit @exit_code if @exit_code && @exit_code != 0
70
- @exit_code || 0
71
- end
72
- rescue WinRM::WinRMHTTPTransportError => e
73
- case e.message
74
- when /401/
75
- if ! config[:suppress_auth_failure]
76
- # Display errors if the caller hasn't opted to retry
77
- ui.error "Failed to authenticate to #{@name_args[0].split(" ")} as #{locate_config_value(:winrm_user)}"
78
- ui.info "Response: #{e.message}"
79
- ui.info "Hint: Please check winrm configuration 'winrm get winrm/config/service' AllowUnencrypted flag on remote server."
80
- raise e
81
- end
82
- @exit_code = 401
83
- else
84
- raise e
85
- end
86
- end
87
- end
88
-
89
- def relay_winrm_command(command)
90
- Chef::Log.debug(command)
91
- @winrm_sessions.each do |s|
92
- s.relay_command(command)
93
- end
94
- end
95
-
96
- # TODO: Copied from Knife::Core:GenericPresenter. Should be extracted
97
- def extract_nested_value(data, nested_value_spec)
98
- nested_value_spec.split(".").each do |attr|
99
- if data.nil?
100
- nil # don't get no method error on nil
101
- elsif data.respond_to?(attr.to_sym)
102
- data = data.send(attr.to_sym)
103
- elsif data.respond_to?(:[])
104
- data = data[attr]
105
- else
106
- data = begin
107
- data.send(attr.to_sym)
108
- rescue NoMethodError
109
- nil
110
- end
111
- end
112
- end
113
- ( !data.kind_of?(Array) && data.respond_to?(:to_hash) ) ? data.to_hash : data
114
- end
115
-
116
- private
117
-
118
- def interactive
119
- puts "WARN: Deprecated functionality. This will not be supported in future knife-windows releases."
120
- puts "Connected to #{ui.list(session.servers.collect { |s| ui.color(s.host, :cyan) }, :inline, " and ")}"
121
- puts
122
- puts "To run a command on a list of servers, do:"
123
- puts " on SERVER1 SERVER2 SERVER3; COMMAND"
124
- puts " Example: on latte foamy; echo foobar"
125
- puts
126
- puts "To exit interactive mode, use 'quit!'"
127
- puts
128
- while 1
129
- command = read_line
130
- case command
131
- when 'quit!'
132
- puts 'Bye!'
133
- break
134
- when /^on (.+?); (.+)$/
135
- raw_list = $1.split(" ")
136
- server_list = Array.new
137
- @winrm_sessions.each do |session_server|
138
- server_list << session_server if raw_list.include?(session_server.host)
139
- end
140
- command = $2
141
- relay_winrm_command(command, server_list)
142
- else
143
- relay_winrm_command(command)
144
- end
145
- end
146
- end
147
-
148
- def check_for_errors!
149
- @winrm_sessions.each do |session|
150
- session_exit_code = session.exit_code
151
- unless success_return_codes.include? session_exit_code.to_i
152
- @exit_code = session_exit_code.to_i
153
- ui.error "Failed to execute command on #{session.host} return code #{session_exit_code}"
154
- end
155
- end
156
- end
157
-
158
- # Present the prompt and read a single line from the console. It also
159
- # detects ^D and returns "exit" in that case. Adds the input to the
160
- # history, unless the input is empty. Loops repeatedly until a non-empty
161
- # line is input.
162
- def read_line
163
- loop do
164
- command = reader.readline("#{ui.color('knife-winrm>', :bold)} ", true)
165
-
166
- if command.nil?
167
- command = "exit"
168
- puts(command)
169
- else
170
- command.strip!
171
- end
172
-
173
- unless command.empty?
174
- return command
175
- end
176
- end
177
- end
178
-
179
- def reader
180
- Readline
181
- end
182
-
183
- def success_return_codes
184
- #Redundant if the CLI options parsing occurs
185
- return [0] unless config[:returns]
186
- return @success_return_codes ||= config[:returns].split(',').collect {|item| item.to_i}
187
- end
188
- end
189
- end
190
- end
191
-
1
+ #
2
+ # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
+ # Copyright:: Copyright (c) 2011 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/knife'
20
+ require 'chef/knife/winrm_knife_base'
21
+ require 'chef/knife/windows_cert_generate'
22
+ require 'chef/knife/windows_cert_install'
23
+ require 'chef/knife/windows_listener_create'
24
+ require 'chef/knife/winrm_session'
25
+ require 'chef/knife/knife_windows_base'
26
+
27
+ class Chef
28
+ class Knife
29
+ class Winrm < Knife
30
+
31
+ include Chef::Knife::WinrmCommandSharedFunctions
32
+ include Chef::Knife::KnifeWindowsBase
33
+
34
+ FAILED_BASIC_HINT ||= "Hint: Please check winrm configuration 'winrm get winrm/config/service' AllowUnencrypted flag on remote server."
35
+ FAILED_NOT_BASIC_HINT ||= <<-eos.gsub /^\s+/, ""
36
+ Hint: Make sure to prefix domain usernames with the correct domain name.
37
+ Hint: Local user names should be prefixed with computer name or IP address.
38
+ EXAMPLE: my_domain\\user_namer
39
+ eos
40
+
41
+ deps do
42
+ require 'readline'
43
+ require 'chef/search/query'
44
+ end
45
+
46
+ attr_writer :password
47
+
48
+ banner "knife winrm QUERY COMMAND (options)"
49
+
50
+ option :returns,
51
+ :long => "--returns CODES",
52
+ :description => "A comma delimited list of return codes which indicate success",
53
+ :default => "0"
54
+
55
+ def run
56
+ STDOUT.sync = STDERR.sync = true
57
+
58
+ configure_session
59
+ validate_password
60
+ execute_remote_command
61
+ end
62
+
63
+ def execute_remote_command
64
+ begin
65
+ case @name_args[1]
66
+ when "interactive"
67
+ interactive
68
+ else
69
+ relay_winrm_command(@name_args[1..-1].join(" "))
70
+
71
+ if config[:returns]
72
+ check_for_errors!
73
+ end
74
+
75
+ # Knife seems to ignore the return value of this method,
76
+ # so we exit to force the process exit code for this
77
+ # subcommand if returns is set
78
+ exit @exit_code if @exit_code && @exit_code != 0
79
+ @exit_code || 0
80
+ end
81
+ rescue WinRM::WinRMHTTPTransportError, WinRM::WinRMAuthorizationError => e
82
+ if authorization_error?(e)
83
+ if ! config[:suppress_auth_failure]
84
+ # Display errors if the caller hasn't opted to retry
85
+ ui.error "Failed to authenticate to #{@name_args[0].split(" ")} as #{locate_config_value(:winrm_user)}"
86
+ ui.info "Response: #{e.message}"
87
+ ui.info get_failed_authentication_hint
88
+ raise e
89
+ end
90
+ @exit_code = 401
91
+ else
92
+ raise e
93
+ end
94
+ end
95
+ end
96
+
97
+ def relay_winrm_command(command)
98
+ Chef::Log.debug(command)
99
+ @winrm_sessions.each do |s|
100
+ s.relay_command(command)
101
+ end
102
+ end
103
+
104
+ # TODO: Copied from Knife::Core:GenericPresenter. Should be extracted
105
+ def extract_nested_value(data, nested_value_spec)
106
+ nested_value_spec.split(".").each do |attr|
107
+ if data.nil?
108
+ nil # don't get no method error on nil
109
+ elsif data.respond_to?(attr.to_sym)
110
+ data = data.send(attr.to_sym)
111
+ elsif data.respond_to?(:[])
112
+ data = data[attr]
113
+ else
114
+ data = begin
115
+ data.send(attr.to_sym)
116
+ rescue NoMethodError
117
+ nil
118
+ end
119
+ end
120
+ end
121
+ ( !data.kind_of?(Array) && data.respond_to?(:to_hash) ) ? data.to_hash : data
122
+ end
123
+
124
+ private
125
+
126
+ def interactive
127
+ puts "WARN: Deprecated functionality. This will not be supported in future knife-windows releases."
128
+ puts "Connected to #{ui.list(session.servers.collect { |s| ui.color(s.host, :cyan) }, :inline, " and ")}"
129
+ puts
130
+ puts "To run a command on a list of servers, do:"
131
+ puts " on SERVER1 SERVER2 SERVER3; COMMAND"
132
+ puts " Example: on latte foamy; echo foobar"
133
+ puts
134
+ puts "To exit interactive mode, use 'quit!'"
135
+ puts
136
+ while 1
137
+ command = read_line
138
+ case command
139
+ when 'quit!'
140
+ puts 'Bye!'
141
+ break
142
+ when /^on (.+?); (.+)$/
143
+ raw_list = $1.split(" ")
144
+ server_list = Array.new
145
+ @winrm_sessions.each do |session_server|
146
+ server_list << session_server if raw_list.include?(session_server.host)
147
+ end
148
+ command = $2
149
+ relay_winrm_command(command, server_list)
150
+ else
151
+ relay_winrm_command(command)
152
+ end
153
+ end
154
+ end
155
+
156
+ def check_for_errors!
157
+ @winrm_sessions.each do |session|
158
+ session_exit_code = session.exit_code
159
+ unless success_return_codes.include? session_exit_code.to_i
160
+ @exit_code = session_exit_code.to_i
161
+ ui.error "Failed to execute command on #{session.host} return code #{session_exit_code}"
162
+ end
163
+ end
164
+ end
165
+
166
+ # Present the prompt and read a single line from the console. It also
167
+ # detects ^D and returns "exit" in that case. Adds the input to the
168
+ # history, unless the input is empty. Loops repeatedly until a non-empty
169
+ # line is input.
170
+ def read_line
171
+ loop do
172
+ command = reader.readline("#{ui.color('knife-winrm>', :bold)} ", true)
173
+
174
+ if command.nil?
175
+ command = "exit"
176
+ puts(command)
177
+ else
178
+ command.strip!
179
+ end
180
+
181
+ unless command.empty?
182
+ return command
183
+ end
184
+ end
185
+ end
186
+
187
+ def reader
188
+ Readline
189
+ end
190
+
191
+ def authorization_error?(exception)
192
+ exception.is_a?(WinRM::WinRMAuthorizationError) ||
193
+ exception.message =~ /401/
194
+ end
195
+
196
+ def success_return_codes
197
+ #Redundant if the CLI options parsing occurs
198
+ return [0] unless config[:returns]
199
+ return @success_return_codes ||= config[:returns].split(',').collect {|item| item.to_i}
200
+ end
201
+
202
+ def get_failed_authentication_hint
203
+ if @session_opts[:basic_auth_only]
204
+ FAILED_BASIC_HINT
205
+ else
206
+ FAILED_NOT_BASIC_HINT
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end
212
+