right_agent 2.0.7-x86-mingw32

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.
Files changed (176) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +82 -0
  3. data/Rakefile +113 -0
  4. data/lib/right_agent.rb +59 -0
  5. data/lib/right_agent/actor.rb +182 -0
  6. data/lib/right_agent/actor_registry.rb +76 -0
  7. data/lib/right_agent/actors/agent_manager.rb +232 -0
  8. data/lib/right_agent/agent.rb +1149 -0
  9. data/lib/right_agent/agent_config.rb +480 -0
  10. data/lib/right_agent/agent_identity.rb +210 -0
  11. data/lib/right_agent/agent_tag_manager.rb +237 -0
  12. data/lib/right_agent/audit_formatter.rb +107 -0
  13. data/lib/right_agent/clients.rb +31 -0
  14. data/lib/right_agent/clients/api_client.rb +383 -0
  15. data/lib/right_agent/clients/auth_client.rb +247 -0
  16. data/lib/right_agent/clients/balanced_http_client.rb +369 -0
  17. data/lib/right_agent/clients/base_retry_client.rb +495 -0
  18. data/lib/right_agent/clients/right_http_client.rb +279 -0
  19. data/lib/right_agent/clients/router_client.rb +493 -0
  20. data/lib/right_agent/command.rb +30 -0
  21. data/lib/right_agent/command/agent_manager_commands.rb +150 -0
  22. data/lib/right_agent/command/command_client.rb +136 -0
  23. data/lib/right_agent/command/command_constants.rb +33 -0
  24. data/lib/right_agent/command/command_io.rb +126 -0
  25. data/lib/right_agent/command/command_parser.rb +87 -0
  26. data/lib/right_agent/command/command_runner.rb +118 -0
  27. data/lib/right_agent/command/command_serializer.rb +63 -0
  28. data/lib/right_agent/connectivity_checker.rb +179 -0
  29. data/lib/right_agent/console.rb +65 -0
  30. data/lib/right_agent/core_payload_types.rb +44 -0
  31. data/lib/right_agent/core_payload_types/cookbook.rb +61 -0
  32. data/lib/right_agent/core_payload_types/cookbook_position.rb +46 -0
  33. data/lib/right_agent/core_payload_types/cookbook_repository.rb +116 -0
  34. data/lib/right_agent/core_payload_types/cookbook_sequence.rb +70 -0
  35. data/lib/right_agent/core_payload_types/dev_repositories.rb +100 -0
  36. data/lib/right_agent/core_payload_types/dev_repository.rb +76 -0
  37. data/lib/right_agent/core_payload_types/event_categories.rb +38 -0
  38. data/lib/right_agent/core_payload_types/executable_bundle.rb +130 -0
  39. data/lib/right_agent/core_payload_types/login_policy.rb +72 -0
  40. data/lib/right_agent/core_payload_types/login_user.rb +79 -0
  41. data/lib/right_agent/core_payload_types/planned_volume.rb +94 -0
  42. data/lib/right_agent/core_payload_types/recipe_instantiation.rb +73 -0
  43. data/lib/right_agent/core_payload_types/repositories_bundle.rb +50 -0
  44. data/lib/right_agent/core_payload_types/right_script_attachment.rb +95 -0
  45. data/lib/right_agent/core_payload_types/right_script_instantiation.rb +94 -0
  46. data/lib/right_agent/core_payload_types/runlist_policy.rb +44 -0
  47. data/lib/right_agent/core_payload_types/secure_document.rb +66 -0
  48. data/lib/right_agent/core_payload_types/secure_document_location.rb +63 -0
  49. data/lib/right_agent/core_payload_types/software_repository_instantiation.rb +61 -0
  50. data/lib/right_agent/daemonize.rb +35 -0
  51. data/lib/right_agent/dispatched_cache.rb +109 -0
  52. data/lib/right_agent/dispatcher.rb +272 -0
  53. data/lib/right_agent/enrollment_result.rb +221 -0
  54. data/lib/right_agent/exceptions.rb +87 -0
  55. data/lib/right_agent/history.rb +145 -0
  56. data/lib/right_agent/log.rb +460 -0
  57. data/lib/right_agent/minimal.rb +46 -0
  58. data/lib/right_agent/monkey_patches.rb +30 -0
  59. data/lib/right_agent/monkey_patches/ruby_patch.rb +55 -0
  60. data/lib/right_agent/monkey_patches/ruby_patch/array_patch.rb +29 -0
  61. data/lib/right_agent/monkey_patches/ruby_patch/darwin_patch.rb +24 -0
  62. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch.rb +24 -0
  63. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch/file_patch.rb +30 -0
  64. data/lib/right_agent/monkey_patches/ruby_patch/object_patch.rb +49 -0
  65. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch.rb +32 -0
  66. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/file_patch.rb +60 -0
  67. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/process_patch.rb +63 -0
  68. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/stdio_patch.rb +27 -0
  69. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/time_patch.rb +55 -0
  70. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/win32ole_patch.rb +34 -0
  71. data/lib/right_agent/multiplexer.rb +102 -0
  72. data/lib/right_agent/offline_handler.rb +270 -0
  73. data/lib/right_agent/operation_result.rb +300 -0
  74. data/lib/right_agent/packets.rb +673 -0
  75. data/lib/right_agent/payload_formatter.rb +104 -0
  76. data/lib/right_agent/pending_requests.rb +128 -0
  77. data/lib/right_agent/pid_file.rb +159 -0
  78. data/lib/right_agent/platform.rb +770 -0
  79. data/lib/right_agent/platform/unix/darwin/platform.rb +102 -0
  80. data/lib/right_agent/platform/unix/linux/platform.rb +305 -0
  81. data/lib/right_agent/platform/unix/platform.rb +226 -0
  82. data/lib/right_agent/platform/windows/mingw/platform.rb +447 -0
  83. data/lib/right_agent/platform/windows/mswin/platform.rb +236 -0
  84. data/lib/right_agent/platform/windows/platform.rb +1808 -0
  85. data/lib/right_agent/protocol_version_mixin.rb +69 -0
  86. data/lib/right_agent/retryable_request.rb +195 -0
  87. data/lib/right_agent/scripts/agent_controller.rb +543 -0
  88. data/lib/right_agent/scripts/agent_deployer.rb +400 -0
  89. data/lib/right_agent/scripts/common_parser.rb +160 -0
  90. data/lib/right_agent/scripts/log_level_manager.rb +192 -0
  91. data/lib/right_agent/scripts/stats_manager.rb +268 -0
  92. data/lib/right_agent/scripts/usage.rb +58 -0
  93. data/lib/right_agent/secure_identity.rb +92 -0
  94. data/lib/right_agent/security.rb +32 -0
  95. data/lib/right_agent/security/cached_certificate_store_proxy.rb +77 -0
  96. data/lib/right_agent/security/certificate.rb +102 -0
  97. data/lib/right_agent/security/certificate_cache.rb +89 -0
  98. data/lib/right_agent/security/distinguished_name.rb +56 -0
  99. data/lib/right_agent/security/encrypted_document.rb +83 -0
  100. data/lib/right_agent/security/rsa_key_pair.rb +76 -0
  101. data/lib/right_agent/security/signature.rb +86 -0
  102. data/lib/right_agent/security/static_certificate_store.rb +85 -0
  103. data/lib/right_agent/sender.rb +792 -0
  104. data/lib/right_agent/serialize.rb +29 -0
  105. data/lib/right_agent/serialize/message_pack.rb +107 -0
  106. data/lib/right_agent/serialize/secure_serializer.rb +151 -0
  107. data/lib/right_agent/serialize/secure_serializer_initializer.rb +47 -0
  108. data/lib/right_agent/serialize/serializable.rb +151 -0
  109. data/lib/right_agent/serialize/serializer.rb +159 -0
  110. data/lib/right_agent/subprocess.rb +38 -0
  111. data/lib/right_agent/tracer.rb +124 -0
  112. data/right_agent.gemspec +101 -0
  113. data/spec/actor_registry_spec.rb +80 -0
  114. data/spec/actor_spec.rb +162 -0
  115. data/spec/agent_config_spec.rb +235 -0
  116. data/spec/agent_identity_spec.rb +78 -0
  117. data/spec/agent_spec.rb +734 -0
  118. data/spec/agent_tag_manager_spec.rb +319 -0
  119. data/spec/clients/api_client_spec.rb +423 -0
  120. data/spec/clients/auth_client_spec.rb +272 -0
  121. data/spec/clients/balanced_http_client_spec.rb +576 -0
  122. data/spec/clients/base_retry_client_spec.rb +635 -0
  123. data/spec/clients/router_client_spec.rb +594 -0
  124. data/spec/clients/spec_helper.rb +111 -0
  125. data/spec/command/agent_manager_commands_spec.rb +51 -0
  126. data/spec/command/command_io_spec.rb +93 -0
  127. data/spec/command/command_parser_spec.rb +79 -0
  128. data/spec/command/command_runner_spec.rb +107 -0
  129. data/spec/command/command_serializer_spec.rb +51 -0
  130. data/spec/connectivity_checker_spec.rb +83 -0
  131. data/spec/core_payload_types/dev_repositories_spec.rb +64 -0
  132. data/spec/core_payload_types/dev_repository_spec.rb +33 -0
  133. data/spec/core_payload_types/executable_bundle_spec.rb +67 -0
  134. data/spec/core_payload_types/login_user_spec.rb +102 -0
  135. data/spec/core_payload_types/recipe_instantiation_spec.rb +81 -0
  136. data/spec/core_payload_types/right_script_attachment_spec.rb +65 -0
  137. data/spec/core_payload_types/right_script_instantiation_spec.rb +79 -0
  138. data/spec/core_payload_types/spec_helper.rb +23 -0
  139. data/spec/dispatched_cache_spec.rb +136 -0
  140. data/spec/dispatcher_spec.rb +324 -0
  141. data/spec/enrollment_result_spec.rb +53 -0
  142. data/spec/history_spec.rb +246 -0
  143. data/spec/log_spec.rb +192 -0
  144. data/spec/monkey_patches/eventmachine_spec.rb +62 -0
  145. data/spec/multiplexer_spec.rb +48 -0
  146. data/spec/offline_handler_spec.rb +340 -0
  147. data/spec/operation_result_spec.rb +208 -0
  148. data/spec/packets_spec.rb +461 -0
  149. data/spec/pending_requests_spec.rb +136 -0
  150. data/spec/platform/spec_helper.rb +216 -0
  151. data/spec/platform/unix/darwin/platform_spec.rb +181 -0
  152. data/spec/platform/unix/linux/platform_spec.rb +540 -0
  153. data/spec/platform/unix/spec_helper.rb +149 -0
  154. data/spec/platform/windows/mingw/platform_spec.rb +222 -0
  155. data/spec/platform/windows/mswin/platform_spec.rb +259 -0
  156. data/spec/platform/windows/spec_helper.rb +720 -0
  157. data/spec/retryable_request_spec.rb +306 -0
  158. data/spec/secure_identity_spec.rb +50 -0
  159. data/spec/security/cached_certificate_store_proxy_spec.rb +62 -0
  160. data/spec/security/certificate_cache_spec.rb +71 -0
  161. data/spec/security/certificate_spec.rb +49 -0
  162. data/spec/security/distinguished_name_spec.rb +46 -0
  163. data/spec/security/encrypted_document_spec.rb +55 -0
  164. data/spec/security/rsa_key_pair_spec.rb +55 -0
  165. data/spec/security/signature_spec.rb +66 -0
  166. data/spec/security/static_certificate_store_spec.rb +58 -0
  167. data/spec/sender_spec.rb +1045 -0
  168. data/spec/serialize/message_pack_spec.rb +131 -0
  169. data/spec/serialize/secure_serializer_spec.rb +132 -0
  170. data/spec/serialize/serializable_spec.rb +90 -0
  171. data/spec/serialize/serializer_spec.rb +197 -0
  172. data/spec/spec.opts +2 -0
  173. data/spec/spec.win32.opts +1 -0
  174. data/spec/spec_helper.rb +130 -0
  175. data/spec/tracer_spec.rb +114 -0
  176. metadata +447 -0
@@ -0,0 +1,720 @@
1
+ #
2
+ # Copyright (c) 2013 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require ::File.expand_path('../../spec_helper', __FILE__)
24
+
25
+ shared_examples_for 'supports windows platform statics' do
26
+ it 'should be Windows' do
27
+ subject.unix?.should be_false
28
+ subject.windows?.should be_true
29
+ subject.linux?.should be_false
30
+ subject.darwin?.should be_false
31
+ end
32
+ end # supports windows platform statics
33
+
34
+ shared_examples_for 'supports windows platform initialization' do
35
+ it 'should query os info during initialize' do
36
+ instrument_os_info(6, 1, 7601)
37
+ platform_instance.send(:initialize_genus)
38
+ platform_instance.release.should == '6.1.7601'
39
+ platform_instance.codename.should == ''
40
+ end
41
+ end # supports windows platform initialization
42
+
43
+ shared_examples_for 'supports windows platform filesystem' do
44
+ context '#find_executable_in_path' do
45
+ let(:pathexts) { %w[.EXE .BAT] }
46
+ let(:paths) { %w[C:\Windows\system32 C:\Windows C:\Windows\System32\Wbem] }
47
+ let(:pathext_env) { pathexts.join(';') }
48
+ let(:path_env) { paths.join(';') }
49
+ let(:expected_path) { 'C:/Windows/foo.EXE' }
50
+
51
+ it 'should find executable' do
52
+ mock_env = flexmock(::ENV)
53
+ mock_env.should_receive(:[]).with('PATHEXT').and_return(pathext_env)
54
+ mock_env.should_receive(:[]).with('PATH').and_return(path_env)
55
+
56
+ all_paths = [::Dir.getwd.gsub("\\", '/')] + paths
57
+ foo_found = false
58
+ all_paths.each do |path|
59
+ pathexts.each do |pathext|
60
+ unless foo_found
61
+ foo_path = ::File.join(path.gsub("\\", '/'), 'foo' + pathext)
62
+ foo_matched = foo_path == expected_path
63
+ file_class.should_receive(:executable?).
64
+ with(foo_path).
65
+ and_return(foo_matched).
66
+ twice
67
+ foo_found = foo_matched
68
+ end
69
+ bar_path = ::File.join(path.gsub("\\", '/'), 'bar' + pathext)
70
+ file_class.should_receive(:executable?).with(bar_path).and_return(false).twice
71
+ end
72
+ end
73
+
74
+ subject.find_executable_in_path('foo').should == expected_path
75
+ subject.has_executable_in_path('foo').should be_true
76
+ subject.find_executable_in_path('bar').should be_nil
77
+ subject.has_executable_in_path('bar').should be_false
78
+ end
79
+ end # find_executable_in_path
80
+
81
+ context 'paths' do
82
+ context 'constants' do
83
+ before(:each) do
84
+ # win32/dir-based constants.
85
+ subject.should_receive(:common_app_data_dir).and_return('C:/ProgramData')
86
+ subject.should_receive(:program_files_dir).and_return('C:/Program Files (x86)')
87
+
88
+ # ENV-based constants.
89
+ @mock_env = flexmock(::ENV)
90
+ @mock_env.should_receive(:[]).with('SystemRoot').and_return("C:\\Windows")
91
+ @mock_env.should_receive(:[]).with('USERPROFILE').and_return("C:\\Users\\foo")
92
+ end
93
+
94
+ it 'should return windows path constants' do
95
+ # windows-specific constants.
96
+ subject.company_app_data_dir.should == 'C:/ProgramData/RightScale'
97
+ subject.company_program_files_dir.should == 'C:/Program Files (x86)/RightScale'
98
+ subject.system_root.should == 'C:/Windows'
99
+ subject.user_home_dir.should == 'C:/Users/foo'
100
+
101
+ # constants available to all platforms.
102
+ subject.right_agent_cfg_dir.should == 'C:/ProgramData/RightScale/right_agent'
103
+ subject.right_scale_static_state_dir.should == 'C:/ProgramData/RightScale/rightscale.d'
104
+ subject.right_link_static_state_dir.should == 'C:/ProgramData/RightScale/rightscale.d/right_link'
105
+ subject.right_link_dynamic_state_dir.should == 'C:/ProgramData/RightScale/right_link'
106
+ subject.spool_dir.should == 'C:/ProgramData/RightScale/spool'
107
+ subject.ssh_cfg_dir.should == 'C:/Users/foo/.ssh'
108
+ subject.cache_dir.should == 'C:/ProgramData/RightScale/cache'
109
+ subject.log_dir.should == 'C:/ProgramData/RightScale/log'
110
+ subject.source_code_dir.should == 'C:/ProgramData/RightScale/src'
111
+ subject.pid_dir.should == 'C:/ProgramData/RightScale/run'
112
+
113
+ @mock_env.
114
+ should_receive(:[]).
115
+ with('RS_RIGHT_LINK_HOME').
116
+ and_return("C:\\PROGRA~2\\RIGHTS~1\\RIGHTL~1")
117
+ file_class.
118
+ should_receive(:normalize_path).
119
+ with('C:/PROGRA~2/RIGHTS~1/RIGHTL~1').
120
+ and_return('C:/PROGRA~2/RIGHTS~1/RIGHTL~1').
121
+ once
122
+ subject.right_link_home_dir.should == 'C:/PROGRA~2/RIGHTS~1/RIGHTL~1'
123
+ subject.private_bin_dir.should == 'C:/PROGRA~2/RIGHTS~1/RIGHTL~1/bin'
124
+ subject.sandbox_dir.should == 'C:/PROGRA~2/RIGHTS~1/RIGHTL~1/sandbox'
125
+ end
126
+
127
+ it 'should generate right_link_home_dir when not set in ENV' do
128
+ @mock_env.should_receive(:[]).with('RS_RIGHT_LINK_HOME').and_return(nil)
129
+ file_class.
130
+ should_receive(:normalize_path).
131
+ with('C:/Program Files (x86)/RightScale/RightLink').
132
+ and_return('C:/PROGRA~2/RIGHTS~1/RIGHTL~1').
133
+ once
134
+ subject.right_link_home_dir.should == 'C:/PROGRA~2/RIGHTS~1/RIGHTL~1'
135
+ end
136
+ end
137
+
138
+ context '#temp_dir' do
139
+ it 'should query temp_dir by calling GetTempPath API' do
140
+ temp_path_from_api = "C:\\Users\\foo\\AppData\\Local\\Temp\\"
141
+ instrument_get_temp_path_api(temp_path_from_api) { temp_path_from_api.length }
142
+ subject.temp_dir.should == 'C:/Users/foo/AppData/Local/Temp'
143
+ end
144
+ end
145
+
146
+ context '#long_path_to_short_path' do
147
+ it 'should convert existing long path to short path' do
148
+ long_path = 'C:\Program Files (x86)\RightScale\RightLink'
149
+ short_path = 'C:/PROGRA~2/RIGHTS~1/RIGHTL~1'
150
+ instrument_get_short_path_name_api(long_path, short_path) { short_path.length }
151
+ file_class.should_receive(:exists?).with(long_path).and_return(true).once
152
+ subject.long_path_to_short_path(long_path).should == short_path
153
+ end
154
+
155
+ it 'should convert existing parent of missing long path to short path' do
156
+ long_path_parent = 'C:/Program Files (x86)/RightScale'
157
+ long_path = long_path_parent + '/RightLink'
158
+ short_path_parent = 'C:/PROGRA~2/RIGHTS~1'
159
+ short_path = short_path_parent + '/RightLink'
160
+ instrument_get_short_path_name_api(long_path_parent, short_path_parent) { short_path_parent.length }
161
+ file_class.should_receive(:exists?).with(long_path).and_return(false).once
162
+ file_class.should_receive(:exists?).with(long_path_parent).and_return(true).once
163
+ subject.long_path_to_short_path(long_path).should == short_path
164
+ end
165
+ end
166
+
167
+ context '#pretty_path' do
168
+ it 'should pretty path' do
169
+ ugly = 'c:\/make//\/me\pretty'
170
+ pretty_for_ruby = 'c:/make/me/pretty'
171
+ pretty_for_cmd_exe = 'c:\make\me\pretty'
172
+ subject.pretty_path(ugly).should == pretty_for_ruby
173
+ subject.pretty_path(ugly, true).should == pretty_for_cmd_exe
174
+ subject.pretty_path(pretty_for_ruby) == pretty_for_ruby
175
+ subject.pretty_path(pretty_for_cmd_exe) == pretty_for_ruby
176
+ subject.pretty_path(pretty_for_ruby, true) == pretty_for_cmd_exe
177
+ subject.pretty_path(pretty_for_cmd_exe, true) == pretty_for_cmd_exe
178
+ end
179
+ end
180
+
181
+ context '#ensure_local_drive_path' do
182
+ before(:each) do
183
+ flexmock(::ENV).should_receive(:[]).with('HOMEDRIVE').and_return('C:')
184
+ end
185
+
186
+ it 'should do nothing if path is already on home drive' do
187
+ path = 'c:\already\local.txt'
188
+ subject.ensure_local_drive_path(path, 'foo').should == path
189
+ end
190
+
191
+ it 'should copy file to temp directory if not local' do
192
+ network_path = 'z:/from/network/drive.txt'
193
+ local_path = 'c:/temp/foo/drive.txt'
194
+ subject.should_receive(:temp_dir).and_return('c:/temp')
195
+ mock_file_utils = flexmock(::FileUtils)
196
+ mock_file_utils.should_receive(:mkdir_p).with('c:/temp/foo').and_return(true).once
197
+ file_class.should_receive(:directory?).with(network_path).and_return(false).once
198
+ mock_file_utils.should_receive(:cp).with(network_path, local_path).and_return(true).once
199
+ subject.ensure_local_drive_path(network_path, 'foo').should == local_path
200
+ end
201
+ end
202
+ end # paths
203
+
204
+ context '#create_symlink' do
205
+ it 'should create directory symlink' do
206
+ from_path = 'c:\foo\bar'
207
+ to_path = 'c:\foo\baz'
208
+ file_class.should_receive(:directory?).with(from_path).and_return(true)
209
+ instrument_create_symbolic_link_api(from_path, to_path, 1) { 1 }
210
+ subject.create_symlink(from_path, to_path).should == 0
211
+ end
212
+
213
+ it 'should create file symlink' do
214
+ from_path = 'c:\foo\bar.txt'
215
+ to_path = 'c:\baz\bar.txt'
216
+ file_class.should_receive(:directory?).with(from_path).and_return(false)
217
+ instrument_create_symbolic_link_api(from_path, to_path, 0) { 1 }
218
+ subject.create_symlink(from_path, to_path).should == 0
219
+ end
220
+
221
+ it 'should raise Win32Error on failure' do
222
+ from_path = 'c:\some\file.txt'
223
+ to_path = 'c:\no_access\hmm.txt'
224
+ file_class.should_receive(:directory?).with(from_path).and_return(false)
225
+ instrument_create_symbolic_link_api(from_path, to_path, 0) { 0 }
226
+ instrument_win32_error(5, 'Access is denied.')
227
+ expect { subject.create_symlink(from_path, to_path) }.
228
+ to raise_error(
229
+ described_class::Win32Error,
230
+ "Failed to create link from #{from_path.inspect} to #{to_path.inspect}\nWin32 error code = 5\nAccess is denied.")
231
+ end
232
+ end # create_symlink
233
+ end # supports windows platform filesystem
234
+
235
+ shared_examples_for 'supports windows platform shell' do
236
+ # required by 'supports any platform shell'
237
+ def instrument_booted_at(booted_at)
238
+ # example:
239
+ # >echo | wmic OS Get LastBootUpTime 2>&1
240
+ # LastBootUpTime
241
+ # 20131025093127.375199-420
242
+ bat = ::Time.at(booted_at.to_i) # same bat time ...
243
+ platform_class.
244
+ should_receive(:execute).
245
+ with('echo | wmic OS Get LastBootUpTime 2>&1', {}).
246
+ and_return(
247
+ '%04d%02d%02d%02d%02d%02d.%06d%03d' %
248
+ [bat.year, bat.month, bat.day,
249
+ bat.hour, bat.min, bat.sec,
250
+ bat.usec, bat.utc_offset / 60]
251
+ )
252
+ end
253
+
254
+ it_should_behave_like 'supports any platform shell'
255
+
256
+ context 'paths' do
257
+ before(:each) do
258
+ # win32/dir-based constants.
259
+ flexmock(platform_class.filesystem).
260
+ should_receive(:sandbox_dir).
261
+ and_return('C:/PROGRA~2/RIGHTS~1/RIGHTL~1/sandbox')
262
+
263
+ # ENV-based constants.
264
+ @mock_env = flexmock(::ENV)
265
+ @mock_env.should_receive(:[]).
266
+ with('RS_RUBY_EXE').
267
+ and_return('C:/PROGRA~2/RIGHTS~1/RIGHTL~1/sandbox/ruby/bin/ruby.exe').
268
+ by_default
269
+ end
270
+
271
+ it 'should return path constants for windows' do
272
+ subject.null_output_name.should == 'NUL'
273
+ file_class.
274
+ should_receive(:normalize_path).
275
+ with('C:/PROGRA~2/RIGHTS~1/RIGHTL~1/sandbox/ruby/bin/ruby.exe').
276
+ and_return('C:/PROGRA~2/RIGHTS~1/RIGHTL~1/sandbox/ruby/bin/ruby.exe').
277
+ once
278
+ subject.sandbox_ruby.should == 'C:/PROGRA~2/RIGHTS~1/RIGHTL~1/sandbox/ruby/bin/ruby.exe'
279
+ end
280
+
281
+ it 'should generate right_link_home_dir when not set in ENV' do
282
+ @mock_env.should_receive(:[]).with('RS_RUBY_EXE').and_return(nil)
283
+ file_class.
284
+ should_receive(:normalize_path).
285
+ with('C:/PROGRA~2/RIGHTS~1/RIGHTL~1/sandbox/ruby/bin/ruby.exe').
286
+ and_return('C:/PROGRA~2/RIGHTS~1/RIGHTL~1/sandbox/ruby/bin/ruby.exe').
287
+ once
288
+ subject.sandbox_ruby.should == 'C:/PROGRA~2/RIGHTS~1/RIGHTL~1/sandbox/ruby/bin/ruby.exe'
289
+ end
290
+ end # paths
291
+
292
+ context 'commands' do
293
+ context '#format_script_file_name' do
294
+ let(:pathexts) { %w[.EXE .BAT] }
295
+ let(:pathext_env) { pathexts.join(';') }
296
+
297
+ before(:each) do
298
+ flexmock(::ENV).should_receive(:[]).with('PATHEXT').and_return(pathext_env)
299
+ end
300
+
301
+ it 'should format for powershell by default' do
302
+ subject.format_script_file_name('foo').should == 'foo.ps1'
303
+ end
304
+
305
+ it 'should keep extension when executable' do
306
+ subject.format_script_file_name('foo.bat').should == 'foo.bat'
307
+ end
308
+
309
+ it 'should append default extension when not executable' do
310
+ subject.format_script_file_name('foo.bar').should == 'foo.bar.ps1'
311
+ end
312
+ end
313
+
314
+ context '#format_executable_command' do
315
+ it 'should format using given script path' do
316
+ subject.format_executable_command('foo.bat', 'a/b', 'c d').should == 'foo.bat a/b "c d"'
317
+ end
318
+
319
+ it 'should insert cmd.exe if no extension was given' do
320
+ subject.format_executable_command('foo', 'bar', '1 2').should == 'cmd.exe /C "foo bar "1 2""'
321
+ end
322
+ end
323
+
324
+ context '#format_shell_command' do
325
+ it 'should insert powershell.exe when extension is .ps1' do
326
+ # RightRun runs child processes as 64-bit instead of 32-bit as would be
327
+ # the default behavior when creating a child process from ruby.exe
328
+ rrp = "C:\\PROGRA~1\\RIGHTS~1\\Shared\\RightRun.exe"
329
+ script_path = 'c:\temp\foo.ps1'
330
+ subject.should_receive(:right_run_path).and_return(rrp)
331
+
332
+ # we wrap powershell script execution with additional checks for
333
+ # $LastExitCode and $Error to ensure user script doesn't fail
334
+ # mysteriously without any kind of error reporting (i.e. it saves us
335
+ # some support calls).
336
+ expected_cmd =
337
+ "#{rrp} powershell.exe -command \"&{set-executionpolicy -executionPolicy RemoteSigned -Scope Process;" +
338
+ " &#{script_path} bar; if ($NULL -eq $LastExitCode) { $LastExitCode = 0 };" +
339
+ ' if ((0 -eq $LastExitCode) -and ($Error.Count -gt 0))' +
340
+ " { $RS_message = 'Script exited successfully but $Error contained '+($Error.Count)+' error(s):';" +
341
+ " write-output $RS_message; write-output $Error; $LastExitCode = 1 }; exit $LastExitCode}\""
342
+
343
+ subject.format_shell_command(script_path, 'bar').should == expected_cmd
344
+ end
345
+
346
+ it 'should insert ruby.exe when extension is .rb' do
347
+ ruby_exe_path = 'C:/PROGRA~2/RIGHTS~1/RIGHTL~1/sandbox/ruby/bin/ruby.exe'
348
+ subject.should_receive(:sandbox_ruby).and_return(ruby_exe_path)
349
+ subject.format_shell_command('foo.rb', 'bar').should == "#{ruby_exe_path} foo.rb bar"
350
+ end
351
+
352
+ it 'should format using given script path for other extensions' do
353
+ subject.format_shell_command('foo.bat', 'a/b', 'c d').should == 'foo.bat a/b "c d"'
354
+ end
355
+
356
+ it 'should insert cmd.exe if no extension was given' do
357
+ subject.format_shell_command('foo', 'bar', '1 2').should == 'cmd.exe /C "foo bar "1 2""'
358
+ end
359
+ end
360
+
361
+ context '#format_ruby_command' do
362
+ it 'should format for ruby' do
363
+ ruby_exe_path = 'C:/PROGRA~2/RIGHTS~1/RIGHTL~1/sandbox/ruby/bin/ruby.exe'
364
+ subject.should_receive(:sandbox_ruby).and_return(ruby_exe_path)
365
+ subject.format_ruby_command('foo.rb', 'bar').should == "#{ruby_exe_path} foo.rb bar"
366
+ end
367
+ end
368
+ end # commands
369
+ end # supports windows platform shell
370
+
371
+ shared_examples_for 'supports windows platform controller' do
372
+ context '#reboot' do
373
+ it 'should call InitiateSystemShutdown API' do
374
+ instrument_initiate_system_shutdown_api(true)
375
+ subject.reboot.should be_true
376
+ end
377
+ end # reboot
378
+
379
+ context '#shutdown' do
380
+ it 'should call InitiateSystemShutdown API' do
381
+ instrument_initiate_system_shutdown_api(false)
382
+ subject.shutdown.should be_true
383
+ end
384
+ end # shutdown
385
+ end # controller
386
+
387
+ shared_examples_for 'supports windows platform installer' do
388
+ context :install do
389
+ it 'should raise not implemented' do
390
+ expect { subject.install(%w[foo bar]) }.
391
+ to raise_error(
392
+ ::RightScale::Exceptions::PlatformError,
393
+ 'No package installers supported on Windows')
394
+ end
395
+ end # install
396
+ end # supports windows platform installer
397
+
398
+ shared_examples_for 'supports windows platform rng' do
399
+ context '#pseudorandom_bytes' do
400
+ it 'should generate random bytes using ruby under Windows' do
401
+ a = subject.pseudorandom_bytes(16)
402
+ b = subject.pseudorandom_bytes(16)
403
+ a.bytesize.should == 16
404
+ b.bytesize.should == 16
405
+ a.should_not == b # equality is statistically impossible
406
+ subject.pseudorandom_bytes(256).bytesize.should == 256
407
+ end
408
+ end # pseudorandom_bytes
409
+ end # supports windows platform rng
410
+
411
+ shared_examples_for 'supports windows platform process' do
412
+ context '#resident_set_size' do
413
+ it 'should return resident set size for current process' do
414
+ expected_size = 12345
415
+ instrument_get_process_memory_info_api(expected_size)
416
+ subject.resident_set_size.should == expected_size
417
+ end
418
+ end # pseudorandom_bytes
419
+ end # supports windows platform rng
420
+
421
+ shared_examples_for 'supports windows platform volume manager' do
422
+ let(:win2003_version) do
423
+ ::RightScale::Platform::WindowsSystemInformation::Version.new(5, 2, 3790)
424
+ end
425
+ let(:win2008r2_version) do
426
+ ::RightScale::Platform::WindowsSystemInformation::Version.new(6, 1, 7601)
427
+ end
428
+
429
+ before(:each) do
430
+ flexmock(platform_class.windows_system_information).
431
+ should_receive(:version).
432
+ and_return(win2008r2_version)
433
+ end
434
+
435
+ context :is_attachable_volume_path? do
436
+ it 'allows paths with hyphens and underscores' do
437
+ subject.is_attachable_volume_path?('C:\\Some_crazy-path').should == true
438
+ end
439
+
440
+ it 'allows paths with periods' do
441
+ subject.is_attachable_volume_path?('C:\\Some.crazy.path').should == true
442
+ end
443
+
444
+ it 'allows paths with tildes' do
445
+ subject.is_attachable_volume_path?('C:\\~Some~crazy~path').should == true
446
+ end
447
+ end
448
+
449
+ context :volumes do
450
+ let(:volumes_script) do
451
+ <<EOF
452
+ rescan
453
+ list volume
454
+ EOF
455
+ end
456
+
457
+ it 'can parse volumes from diskpart output' do
458
+ subject.
459
+ should_receive(:run_diskpart_script).
460
+ with(volumes_script, String).
461
+ and_return(
462
+ <<EOF
463
+ Volume ### Ltr Label Fs Type Size Status Info
464
+ ---------- --- ----------- ----- ---------- ------- --------- --------
465
+ Volume 0 C 2008Boot NTFS Partition 80 GB Healthy System
466
+ * Volume 1 D NTFS Partition 4094 MB Healthy
467
+ Volume 2 NTFS Partition 4094 MB Healthy
468
+ EOF
469
+ ).once
470
+ expected_volumes = [
471
+ { :index => 0, :device => 'C:', :label => '2008Boot', :filesystem => 'NTFS', :type => 'Partition', :total_size => 85899345920, :status => 'Healthy', :info => 'System' },
472
+ { :index => 1, :device => 'D:', :label => nil, :filesystem => 'NTFS', :type => 'Partition', :total_size => 4292870144, :status => 'Healthy', :info => nil },
473
+ { :index => 2, :device => nil, :label => nil, :filesystem => 'NTFS', :type => 'Partition', :total_size => 4292870144, :status => 'Healthy', :info => nil }
474
+ ]
475
+ subject.volumes.should == expected_volumes
476
+ end
477
+
478
+ it 'can parse volumes from diskpart output with mounted paths' do
479
+ subject.
480
+ should_receive(:run_diskpart_script).
481
+ with(volumes_script, String).
482
+ and_return(
483
+ <<EOF
484
+ Volume ### Ltr Label Fs Type Size Status Info
485
+ ---------- --- ----------- ----- ---------- ------- --------- --------
486
+ Volume 0 C NTFS Partition 80 GB Healthy System
487
+ * Volume 1 FAT32 Partition 1023 MB Healthy
488
+ C:\\Program Files\\RightScale\\Mount\\Softlayer\\
489
+ EOF
490
+ ).once
491
+ expected_volumes = [
492
+ { :index => 0, :device => 'C:', :label => nil, :filesystem => 'NTFS', :type => 'Partition', :total_size => 85899345920, :status => 'Healthy', :info => 'System' },
493
+ { :index => 1, :device => 'C:\\Program Files\\RightScale\\Mount\\Softlayer\\', :label => nil, :filesystem => 'FAT32', :type => 'Partition', :total_size => 1072693248, :status => 'Healthy', :info => nil }
494
+ ]
495
+ subject.volumes.should == expected_volumes
496
+ end
497
+
498
+ it 'raises a parser error when diskpart output is malformed' do
499
+ subject.
500
+ should_receive(:run_diskpart_script).
501
+ with(volumes_script, String).
502
+ and_return('gibberish').
503
+ once
504
+ expect { subject.volumes }.
505
+ to raise_error(RightScale::Platform::VolumeManager::ParserError)
506
+ end
507
+
508
+ it 'can filter results with only one condition' do
509
+ subject.
510
+ should_receive(:run_diskpart_script).
511
+ with(volumes_script, String).
512
+ and_return(
513
+ <<EOF
514
+ Volume ### Ltr Label Fs Type Size Status Info
515
+ ---------- --- ----------- ----- ---------- ------- --------- --------
516
+ Volume 0 C 2008Boot NTFS Partition 80 GB Healthy System
517
+ * Volume 1 D NTFS Partition 4094 MB Healthy
518
+ Volume 2 NTFS Partition 4094 MB Healthy
519
+ EOF
520
+ ).once
521
+ expected_volumes = [
522
+ { :index => 1, :device => 'D:', :label => nil, :filesystem => 'NTFS', :type => 'Partition', :total_size => 4292870144, :status => 'Healthy', :info => nil }
523
+ ]
524
+ subject.volumes(:device => 'D:').should == expected_volumes
525
+ end
526
+
527
+ it 'can filter results with many conditions' do
528
+ subject.
529
+ should_receive(:run_diskpart_script).
530
+ with(volumes_script, String).
531
+ and_return(
532
+ <<EOF
533
+ Volume ### Ltr Label Fs Type Size Status Info
534
+ ---------- --- ----------- ----- ---------- ------- --------- --------
535
+ Volume 0 C 2008Boot NTFS Partition 80 GB Healthy System
536
+ * Volume 1 D NTFS Partition 4094 MB Healthy
537
+ Volume 2 NTFS Partition 4094 MB Healthy
538
+ EOF
539
+ ).once
540
+ expected_volumes = [
541
+ {:index => 1, :device => 'D:', :label => nil, :filesystem => 'NTFS', :type => 'Partition', :total_size => 4292870144, :status => 'Healthy', :info => nil}
542
+ ]
543
+ conditions = { :device => 'D:', :filesystem => 'NTFS', :type => 'Partition' }
544
+ subject.volumes(conditions).should == expected_volumes
545
+ end
546
+ end
547
+
548
+ context :assign_device do
549
+ it 'assigns a device to a drive letter when a drive letter is specified' do
550
+ assign_device_script = <<EOF
551
+ rescan
552
+ list volume
553
+ select volume "0"
554
+ attribute volume clear readonly noerr
555
+
556
+ assign letter=S
557
+ EOF
558
+
559
+ subject.
560
+ should_receive(:run_diskpart_script).
561
+ with(assign_device_script, String).
562
+ and_return('').
563
+ once
564
+ subject.assign_device(0, 'S:')
565
+ end
566
+
567
+ it 'assigns a device to a path when a mount point is specified' do
568
+ assign_device_script = <<EOF
569
+ rescan
570
+ list volume
571
+ select volume "2"
572
+ attribute volume clear readonly noerr
573
+
574
+ assign mount="C:\\Program Files\\RightScale\\Mount\\Softlayer"
575
+ EOF
576
+ subject.
577
+ should_receive(:run_diskpart_script).
578
+ with(assign_device_script, String).
579
+ and_return('').
580
+ once
581
+ subject.assign_device(2, "C:\\Program Files\\RightScale\\Mount\\Softlayer")
582
+ end
583
+
584
+ it 'raises an exception when an invalid drive letter is specified' do
585
+ expect { subject.assign_device(0, 'C:') }.
586
+ to raise_error(RightScale::Platform::VolumeManager::ArgumentError)
587
+ end
588
+
589
+ it 'raises an exception when an invalid path is specified' do
590
+ expect { subject.assign_device(0, 'This is not a path') }.
591
+ to raise_error(RightScale::Platform::VolumeManager::ArgumentError)
592
+ end
593
+
594
+ it 'raises an exception when a mount path is specified and OS is pre 2008' do
595
+ subject.should_receive(:os_version).and_return(win2003_version)
596
+ expect { subject.assign_device(0, "C:\\Somepath") }.
597
+ to raise_error(RightScale::Platform::VolumeManager::ArgumentError)
598
+ end
599
+
600
+ it 'does not assign when already assigned by index' do
601
+ subject.should_receive(:run_diskpart_script).never
602
+ subject.
603
+ should_receive(:volumes).
604
+ and_return([{:index => '0', :device => "C:\\Program Files\\RightScale\\Mount\\Softlayer"}]).
605
+ once
606
+ subject.assign_device(0, "C:\\Program Files\\RightScale\\Mount\\Softlayer", :idempotent => true)
607
+ end
608
+
609
+ it 'does not assign when already assigned by device' do
610
+ subject.should_receive(:run_diskpart_script).never
611
+ subject.
612
+ should_receive(:volumes).
613
+ and_return([{:index => '0', :device => 'Z:'}]).
614
+ once
615
+ subject.assign_device('Z:', 'Z:', :idempotent => true)
616
+ end
617
+
618
+ it 'does not clear readonly flag if :clear_readonly option is set to false' do
619
+ assign_device_script = <<EOF
620
+ rescan
621
+ list volume
622
+ select volume "0"
623
+
624
+
625
+ assign mount="C:\\Program Files\\RightScale\\Mount\\Softlayer"
626
+ EOF
627
+ subject.
628
+ should_receive(:run_diskpart_script).
629
+ with(assign_device_script, String).
630
+ and_return('').
631
+ once
632
+ subject.assign_device(0, "C:\\Program Files\\RightScale\\Mount\\Softlayer", :clear_readonly => false)
633
+ end
634
+
635
+ it 'removes all previous assignments if :remove_all option is set to true' do
636
+ assign_device_script = <<EOF
637
+ rescan
638
+ list volume
639
+ select volume "0"
640
+ attribute volume clear readonly noerr
641
+ remove all noerr
642
+ assign mount="C:\\Program Files\\RightScale\\Mount\\Softlayer"
643
+ EOF
644
+ subject.
645
+ should_receive(:run_diskpart_script).
646
+ with(assign_device_script, String).
647
+ and_return('').
648
+ once
649
+ subject.assign_device(0, "C:\\Program Files\\RightScale\\Mount\\Softlayer", :remove_all => true)
650
+ end
651
+ end
652
+
653
+ context :format_disk do
654
+ it 'formats and assigns a drive to a drive letter when a drive letter is specified' do
655
+ assign_device_script = <<EOF
656
+ rescan
657
+ list disk
658
+ select disk 0
659
+ attribute disk clear readonly noerr
660
+ online disk noerr
661
+ clean
662
+ create partition primary
663
+ assign letter=S
664
+ format FS=NTFS quick
665
+ EOF
666
+ subject.
667
+ should_receive(:run_diskpart_script).
668
+ with(assign_device_script, String).
669
+ and_return('').
670
+ once
671
+ subject.format_disk(0, 'S:')
672
+ end
673
+
674
+ it 'formats and assigns a drive to a path when a mount point is specified' do
675
+ assign_device_script = <<EOF
676
+ rescan
677
+ list disk
678
+ select disk 0
679
+ attribute disk clear readonly noerr
680
+ online disk noerr
681
+ clean
682
+ create partition primary
683
+ assign mount="C:\\Somepath"
684
+ format FS=NTFS quick
685
+ EOF
686
+ subject.
687
+ should_receive(:run_diskpart_script).
688
+ with(assign_device_script, String).
689
+ and_return('').
690
+ once
691
+ subject.format_disk(0, 'C:\\Somepath')
692
+ end
693
+
694
+ it 'raises an exception when a mount path is specified and OS is pre 2008' do
695
+ subject.should_receive(:os_version).and_return(win2003_version)
696
+ expect { subject.format_disk(0, "C:\\Somepath") }.
697
+ to raise_error(RightScale::Platform::VolumeManager::ArgumentError)
698
+ end
699
+ end # format_disk
700
+
701
+ context :online_disk do
702
+ it 'does not online the disk if the disk is already online' do
703
+ online_disk_script = <<EOF
704
+ rescan
705
+ list disk
706
+ select disk 0
707
+ attribute disk clear readonly noerr
708
+ online disk noerr
709
+ EOF
710
+ subject.
711
+ should_receive(:run_diskpart_script).
712
+ with(online_disk_script, String).
713
+ and_return('').
714
+ once
715
+ subject.online_disk(0)
716
+ subject.should_receive(:disks).and_return([{:index => 0, :status => 'Online'}]).once
717
+ subject.online_disk(0, :idempotent => true)
718
+ end
719
+ end
720
+ end # supports windows platform volume manager