chef 12.5.1 → 12.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (221) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +6 -1
  3. data/README.md +6 -4
  4. data/Rakefile +1 -4
  5. data/chef-windows.gemspec +21 -0
  6. data/chef.gemspec +58 -0
  7. data/lib/chef/api_client/registration.rb +9 -4
  8. data/lib/chef/application.rb +3 -84
  9. data/lib/chef/application/apply.rb +9 -2
  10. data/lib/chef/application/client.rb +8 -3
  11. data/lib/chef/application/solo.rb +7 -1
  12. data/lib/chef/application/windows_service.rb +21 -6
  13. data/lib/chef/application/windows_service_manager.rb +2 -3
  14. data/lib/chef/audit/runner.rb +1 -0
  15. data/lib/chef/chef_class.rb +1 -11
  16. data/lib/chef/chef_fs/chef_fs_data_store.rb +181 -2
  17. data/lib/chef/chef_fs/file_system/cookbook_subdir.rb +5 -0
  18. data/lib/chef/chef_fs/file_system/file_system_entry.rb +11 -7
  19. data/lib/chef/client.rb +28 -1
  20. data/lib/chef/cookbook/cookbook_collection.rb +14 -1
  21. data/lib/chef/cookbook/cookbook_version_loader.rb +1 -1
  22. data/lib/chef/cookbook/metadata.rb +115 -9
  23. data/lib/chef/cookbook/remote_file_vendor.rb +1 -1
  24. data/lib/chef/cookbook_version.rb +6 -2
  25. data/lib/chef/data_bag.rb +1 -1
  26. data/lib/chef/data_bag_item.rb +1 -1
  27. data/lib/chef/digester.rb +5 -1
  28. data/lib/chef/dsl/chef_provisioning.rb +57 -0
  29. data/lib/chef/dsl/cheffish.rb +64 -0
  30. data/lib/chef/dsl/declare_resource.rb +108 -0
  31. data/lib/chef/dsl/platform_introspection.rb +3 -3
  32. data/lib/chef/dsl/recipe.rb +3 -73
  33. data/lib/chef/dsl/resources.rb +27 -1
  34. data/lib/chef/event_dispatch/base.rb +3 -0
  35. data/lib/chef/event_dispatch/dispatcher.rb +5 -0
  36. data/lib/chef/event_dispatch/events_output_stream.rb +8 -0
  37. data/lib/chef/exceptions.rb +21 -1
  38. data/lib/chef/file_access_control/unix.rb +12 -12
  39. data/lib/chef/file_content_management/deploy/cp.rb +2 -2
  40. data/lib/chef/file_content_management/deploy/mv_unix.rb +4 -4
  41. data/lib/chef/file_content_management/deploy/mv_windows.rb +1 -1
  42. data/lib/chef/formatters/base.rb +7 -0
  43. data/lib/chef/formatters/error_inspectors/compile_error_inspector.rb +2 -2
  44. data/lib/chef/formatters/indentable_output_stream.rb +5 -0
  45. data/lib/chef/http.rb +19 -3
  46. data/lib/chef/http/decompressor.rb +2 -2
  47. data/lib/chef/json_compat.rb +1 -0
  48. data/lib/chef/knife.rb +16 -2
  49. data/lib/chef/knife/bootstrap.rb +55 -10
  50. data/lib/chef/knife/cookbook_site_install.rb +5 -1
  51. data/lib/chef/knife/core/bootstrap_context.rb +2 -1
  52. data/lib/chef/knife/core/node_presenter.rb +1 -1
  53. data/lib/chef/knife/ssh.rb +30 -16
  54. data/lib/chef/knife/ssl_check.rb +4 -2
  55. data/lib/chef/knife/ssl_fetch.rb +3 -2
  56. data/lib/chef/knife/status.rb +14 -1
  57. data/lib/chef/log.rb +14 -0
  58. data/lib/chef/mixin/get_source_from_package.rb +7 -2
  59. data/lib/chef/mixin/properties.rb +302 -0
  60. data/lib/chef/mixin/proxified_socket.rb +38 -0
  61. data/lib/chef/mixin/subclass_directive.rb +37 -0
  62. data/lib/chef/node.rb +13 -5
  63. data/lib/chef/platform/query_helpers.rb +14 -3
  64. data/lib/chef/platform/service_helpers.rb +20 -38
  65. data/lib/chef/policy_builder/expand_node_object.rb +3 -0
  66. data/lib/chef/policy_builder/policyfile.rb +1 -0
  67. data/lib/chef/property.rb +51 -12
  68. data/lib/chef/provider.rb +40 -35
  69. data/lib/chef/provider/deploy.rb +1 -1
  70. data/lib/chef/provider/dsc_resource.rb +54 -20
  71. data/lib/chef/provider/execute.rb +25 -4
  72. data/lib/chef/provider/group.rb +1 -1
  73. data/lib/chef/provider/lwrp_base.rb +1 -0
  74. data/lib/chef/provider/package.rb +76 -30
  75. data/lib/chef/provider/package/dpkg.rb +152 -69
  76. data/lib/chef/provider/package/openbsd.rb +6 -8
  77. data/lib/chef/provider/package/solaris.rb +2 -0
  78. data/lib/chef/provider/package/windows.rb +95 -14
  79. data/lib/chef/provider/package/windows/exe.rb +129 -0
  80. data/lib/chef/provider/package/windows/msi.rb +37 -13
  81. data/lib/chef/provider/package/windows/registry_uninstall_entry.rb +89 -0
  82. data/lib/chef/provider/package/yum.rb +13 -3
  83. data/lib/chef/provider/powershell_script.rb +3 -0
  84. data/lib/chef/provider/remote_file/cache_control_data.rb +37 -4
  85. data/lib/chef/provider/remote_file/http.rb +1 -1
  86. data/lib/chef/provider/script.rb +1 -0
  87. data/lib/chef/provider/service.rb +13 -10
  88. data/lib/chef/provider/service/solaris.rb +43 -17
  89. data/lib/chef/provider/service/upstart.rb +3 -3
  90. data/lib/chef/provider/user.rb +1 -1
  91. data/lib/chef/provider/user/dscl.rb +111 -100
  92. data/lib/chef/provider/user/windows.rb +5 -3
  93. data/lib/chef/recipe.rb +3 -5
  94. data/lib/chef/resource.rb +77 -320
  95. data/lib/chef/resource/action_class.rb +4 -0
  96. data/lib/chef/resource/dpkg_package.rb +4 -3
  97. data/lib/chef/resource/dsc_resource.rb +40 -2
  98. data/lib/chef/resource/execute.rb +9 -1
  99. data/lib/chef/resource/ksh.rb +32 -0
  100. data/lib/chef/resource/lwrp_base.rb +6 -10
  101. data/lib/chef/resource/package.rb +8 -9
  102. data/lib/chef/resource/registry_key.rb +1 -1
  103. data/lib/chef/resource/resource_notification.rb +14 -1
  104. data/lib/chef/resource/script.rb +1 -1
  105. data/lib/chef/resource/windows_package.rb +1 -1
  106. data/lib/chef/resource_builder.rb +14 -7
  107. data/lib/chef/resource_reporter.rb +6 -0
  108. data/lib/chef/resources.rb +1 -7
  109. data/lib/chef/rest.rb +1 -1
  110. data/lib/chef/run_context.rb +45 -2
  111. data/lib/chef/run_list/run_list_expansion.rb +47 -0
  112. data/lib/chef/runner.rb +25 -0
  113. data/lib/chef/search/query.rb +16 -2
  114. data/lib/chef/util/diff.rb +2 -2
  115. data/lib/chef/util/powershell/ps_credential.rb +2 -3
  116. data/lib/chef/version.rb +1 -1
  117. data/lib/chef/win32/api/file.rb +51 -1
  118. data/lib/chef/win32/file.rb +5 -0
  119. data/lib/chef/win32/file/version_info.rb +93 -0
  120. data/lib/chef/win32/mutex.rb +1 -1
  121. data/spec/data/apt/chef-integration-test2-1.0/debian/changelog +5 -0
  122. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.debhelper.log +45 -0
  123. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.substvars +1 -0
  124. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2/DEBIAN/conffiles +1 -0
  125. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2/DEBIAN/control +10 -0
  126. data/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2/DEBIAN/md5sums +1 -0
  127. data/spec/data/apt/chef-integration-test2-1.0/debian/compat +1 -0
  128. data/spec/data/apt/chef-integration-test2-1.0/debian/conffiles +1 -0
  129. data/spec/data/apt/chef-integration-test2-1.0/debian/control +13 -0
  130. data/spec/data/apt/chef-integration-test2-1.0/debian/copyright +34 -0
  131. data/spec/data/apt/chef-integration-test2-1.0/debian/files +1 -0
  132. data/spec/data/apt/chef-integration-test2-1.0/debian/rules +13 -0
  133. data/spec/data/apt/chef-integration-test2-1.0/debian/source/format +1 -0
  134. data/spec/data/apt/chef-integration-test2_1.0-1.debian.tar.gz +0 -0
  135. data/spec/data/apt/chef-integration-test2_1.0-1.dsc +18 -0
  136. data/spec/data/apt/chef-integration-test2_1.0-1_amd64.build +91 -0
  137. data/spec/data/apt/chef-integration-test2_1.0-1_amd64.changes +31 -0
  138. data/spec/data/apt/chef-integration-test2_1.0-1_amd64.deb +0 -0
  139. data/spec/data/apt/chef-integration-test2_1.0.orig.tar.gz +0 -0
  140. data/spec/functional/application_spec.rb +1 -1
  141. data/spec/functional/audit/runner_spec.rb +4 -0
  142. data/spec/functional/knife/ssh_spec.rb +5 -5
  143. data/spec/functional/notifications_spec.rb +74 -4
  144. data/spec/functional/resource/aix_service_spec.rb +2 -2
  145. data/spec/functional/resource/dpkg_package_spec.rb +339 -0
  146. data/spec/functional/resource/ifconfig_spec.rb +3 -1
  147. data/spec/functional/resource/mount_spec.rb +5 -2
  148. data/spec/functional/resource/package_spec.rb +1 -1
  149. data/spec/functional/resource/user/windows_spec.rb +8 -0
  150. data/spec/functional/resource/windows_package_spec.rb +177 -0
  151. data/spec/functional/win32/version_info_spec.rb +50 -0
  152. data/spec/integration/client/client_spec.rb +80 -0
  153. data/spec/integration/knife/download_spec.rb +9 -0
  154. data/spec/integration/knife/upload_spec.rb +28 -1
  155. data/spec/integration/recipes/lwrp_inline_resources_spec.rb +93 -23
  156. data/spec/integration/recipes/resource_action_spec.rb +211 -116
  157. data/spec/integration/recipes/resource_converge_if_changed_spec.rb +72 -0
  158. data/spec/integration/solo/solo_spec.rb +34 -0
  159. data/spec/spec_helper.rb +11 -1
  160. data/spec/support/platform_helpers.rb +8 -0
  161. data/spec/support/shared/integration/integration_helper.rb +6 -0
  162. data/spec/support/shared/unit/execute_resource.rb +5 -0
  163. data/spec/support/shared/unit/platform_introspector.rb +7 -0
  164. data/spec/tiny_server.rb +6 -2
  165. data/spec/unit/api_client/registration_spec.rb +5 -4
  166. data/spec/unit/application_spec.rb +1 -181
  167. data/spec/unit/chef_fs/file_system/cookbook_subdir_spec.rb +34 -0
  168. data/spec/unit/cookbook/metadata_spec.rb +122 -2
  169. data/spec/unit/http_spec.rb +102 -0
  170. data/spec/unit/knife/bootstrap_spec.rb +55 -13
  171. data/spec/unit/knife/core/bootstrap_context_spec.rb +10 -3
  172. data/spec/unit/knife/ssl_check_spec.rb +7 -3
  173. data/spec/unit/knife/ssl_fetch_spec.rb +2 -2
  174. data/spec/unit/knife/status_spec.rb +13 -13
  175. data/spec/unit/knife_spec.rb +26 -2
  176. data/spec/unit/lwrp_spec.rb +1 -1
  177. data/spec/unit/mixin/properties_spec.rb +97 -0
  178. data/spec/unit/mixin/proxified_socket_spec.rb +94 -0
  179. data/spec/unit/mixin/subclass_directive_spec.rb +45 -0
  180. data/spec/unit/node_spec.rb +9 -1
  181. data/spec/unit/policy_builder/policyfile_spec.rb +2 -0
  182. data/spec/unit/property/validation_spec.rb +14 -12
  183. data/spec/unit/property_spec.rb +56 -0
  184. data/spec/unit/provider/deploy_spec.rb +1 -1
  185. data/spec/unit/provider/dsc_resource_spec.rb +63 -24
  186. data/spec/unit/provider/execute_spec.rb +95 -28
  187. data/spec/unit/provider/package/dpkg_spec.rb +185 -96
  188. data/spec/unit/provider/package/windows/exe_spec.rb +251 -0
  189. data/spec/unit/provider/package/windows/msi_spec.rb +94 -10
  190. data/spec/unit/provider/package/windows_spec.rb +227 -26
  191. data/spec/unit/provider/package/yum_spec.rb +6 -0
  192. data/spec/unit/provider/package_spec.rb +495 -366
  193. data/spec/unit/provider/remote_file/cache_control_data_spec.rb +62 -36
  194. data/spec/unit/provider/script_spec.rb +2 -2
  195. data/spec/unit/provider/service/solaris_smf_service_spec.rb +110 -39
  196. data/spec/unit/provider/service/upstart_service_spec.rb +19 -0
  197. data/spec/unit/provider/user/dscl_spec.rb +14 -0
  198. data/spec/unit/provider/user/windows_spec.rb +2 -2
  199. data/spec/unit/provider/user_spec.rb +9 -0
  200. data/spec/unit/provider_resolver_spec.rb +6 -30
  201. data/spec/unit/recipe_spec.rb +46 -20
  202. data/spec/unit/resource/chef_gem_spec.rb +1 -1
  203. data/spec/unit/resource/dsc_resource_spec.rb +14 -3
  204. data/spec/unit/resource/ksh_spec.rb +40 -0
  205. data/spec/unit/resource/registry_key_spec.rb +2 -2
  206. data/spec/unit/resource/resource_notification_spec.rb +44 -45
  207. data/spec/unit/resource_reporter_spec.rb +7 -0
  208. data/spec/unit/resource_spec.rb +268 -253
  209. data/spec/unit/rest_spec.rb +2 -2
  210. data/spec/unit/run_list/run_list_expansion_spec.rb +18 -3
  211. data/spec/unit/search/query_spec.rb +19 -1
  212. data/spec/unit/util/powershell/ps_credential_spec.rb +8 -1
  213. data/spec/unit/windows_service_spec.rb +83 -38
  214. data/tasks/external_tests.rb +19 -9
  215. data/tasks/rspec.rb +1 -1
  216. metadata +64 -15
  217. data/spec/support/pedant/Gemfile +0 -3
  218. data/spec/support/pedant/pedant_config.rb +0 -129
  219. data/spec/support/pedant/run_pedant.rb +0 -63
  220. data/spec/support/pedant/stickywicket.pem +0 -27
  221. data/spec/unit/provider/package_spec.rbe +0 -0
@@ -0,0 +1,251 @@
1
+ #
2
+ # Author:: Matt Wrock <matt@mattwrock.com>
3
+ # Copyright:: Copyright (c) 2015 Chef Software, 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 'spec_helper'
20
+ require 'chef/provider/package/windows/exe'
21
+
22
+ unless Chef::Platform.windows?
23
+ class Chef
24
+ module ReservedNames::Win32
25
+ class File
26
+ def version_info
27
+ nil
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ describe Chef::Provider::Package::Windows::Exe do
35
+ let(:package_name) { "calculator" }
36
+ let(:resource_source) { "calculator.exe" }
37
+ let(:new_resource) do
38
+ new_resource = Chef::Resource::WindowsPackage.new(package_name)
39
+ new_resource.source(resource_source)
40
+ new_resource
41
+ end
42
+ let(:uninstall_hash) do
43
+ [{
44
+ 'DisplayVersion' => 'outdated',
45
+ 'UninstallString' => File.join("uninst_dir", "uninst_file")
46
+ }]
47
+ end
48
+ let(:uninstall_entry) do
49
+ entries = []
50
+ uninstall_hash.each do |entry|
51
+ entries.push(Chef::Provider::Package::Windows::RegistryUninstallEntry.new('hive', 'key', entry))
52
+ end
53
+ entries
54
+ end
55
+ let(:provider) { Chef::Provider::Package::Windows::Exe.new(new_resource, :nsis, uninstall_entry) }
56
+ let(:file_version) { nil }
57
+ let(:product_version) { nil }
58
+ let(:version_info) { instance_double("Chef::ReservedNames::Win32::File::Version_info", FileVersion: file_version, ProductVersion: product_version) }
59
+
60
+ before(:each) do
61
+ allow(Chef::ReservedNames::Win32::File).to receive(:version_info).and_return(version_info)
62
+ allow(::File).to receive(:exist?).with(Chef::Util::PathHelper.canonical_path(resource_source, false)).and_return(true)
63
+ end
64
+
65
+ it "responds to shell_out!" do
66
+ expect(provider).to respond_to(:shell_out!)
67
+ end
68
+
69
+ describe "expand_options" do
70
+ it "returns an empty string if passed no options" do
71
+ expect(provider.expand_options(nil)).to eql ""
72
+ end
73
+
74
+ it "returns a string with a leading space if passed options" do
75
+ expect(provider.expand_options("--train nope --town no_way")).to eql(" --train nope --town no_way")
76
+ end
77
+ end
78
+
79
+ describe "installed_version" do
80
+ it "returns the installed version" do
81
+ expect(provider.installed_version).to eql(["outdated"])
82
+ end
83
+
84
+ context "no versions installed" do
85
+ let(:uninstall_hash) { [] }
86
+
87
+ it "returns the installed version" do
88
+ expect(provider.installed_version).to eql(nil)
89
+ end
90
+ end
91
+ end
92
+
93
+ describe "package_version" do
94
+ before { new_resource.version(nil) }
95
+
96
+ context "source file does not exist" do
97
+ before do
98
+ allow(::File).to receive(:exist?).with(Chef::Util::PathHelper.canonical_path(resource_source, false)).and_return(false)
99
+ end
100
+
101
+ it "returns nil" do
102
+ expect(provider.package_version).to eql(nil)
103
+ end
104
+ end
105
+
106
+ context "file version is empty" do
107
+ let(:file_version) { '' }
108
+
109
+ it "returns nil" do
110
+ expect(provider.package_version).to eql(nil)
111
+ end
112
+
113
+ it "returns the version of a package if given" do
114
+ new_resource.version('v55555')
115
+ expect(provider.package_version).to eql('v55555')
116
+ end
117
+ end
118
+
119
+ context "both file and product version are in installer" do
120
+ let(:file_version) { '1.1.1' }
121
+ let(:product_version) { '1.1' }
122
+
123
+ it "returns the file version" do
124
+ expect(provider.package_version).to eql('1.1.1')
125
+ end
126
+
127
+ it "returns the version of a package if given" do
128
+ new_resource.version('v55555')
129
+ expect(provider.package_version).to eql('v55555')
130
+ end
131
+ end
132
+
133
+ context "only file version is in installer" do
134
+ let(:file_version) { '1.1.1' }
135
+
136
+ it "returns the file version" do
137
+ expect(provider.package_version).to eql('1.1.1')
138
+ end
139
+
140
+ it "returns the version of a package if given" do
141
+ new_resource.version('v55555')
142
+ expect(provider.package_version).to eql('v55555')
143
+ end
144
+ end
145
+
146
+ context "only product version is in installer" do
147
+ let(:product_version) { '1.1' }
148
+
149
+ it "returns the product version" do
150
+ expect(provider.package_version).to eql('1.1')
151
+ end
152
+
153
+ it "returns the version of a package if given" do
154
+ new_resource.version('v55555')
155
+ expect(provider.package_version).to eql('v55555')
156
+ end
157
+ end
158
+
159
+ context "no version info is in installer" do
160
+ let(:file_version) { nil }
161
+ let(:product_version) { nil }
162
+
163
+ it "returns the version of a package" do
164
+ new_resource.version('v55555')
165
+ expect(provider.package_version).to eql('v55555')
166
+ end
167
+ end
168
+
169
+ context "no version info is in installer and none in attribute" do
170
+ it "returns the version of a package" do
171
+ expect(provider.package_version).to eql(nil)
172
+ end
173
+ end
174
+ end
175
+
176
+ describe "remove_package" do
177
+ context "no version given and one package installed" do
178
+ it "removes installed package" do
179
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir\" uninst_file \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
180
+ provider.remove_package
181
+ end
182
+ end
183
+
184
+ context "several packages installed" do
185
+ let(:uninstall_hash) do
186
+ [
187
+ {
188
+ 'DisplayVersion' => 'v1',
189
+ 'UninstallString' => File.join("uninst_dir1", "uninst_file1")
190
+ },
191
+ {
192
+ 'DisplayVersion' => 'v2',
193
+ 'UninstallString' => File.join("uninst_dir2", "uninst_file2")
194
+ }
195
+ ]
196
+ end
197
+
198
+ context "version given and installed" do
199
+ it "removes given version" do
200
+ new_resource.version('v2')
201
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir2\" uninst_file2 \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
202
+ provider.remove_package
203
+ end
204
+ end
205
+
206
+ context "no version given" do
207
+ it "removes both versions" do
208
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir1\" uninst_file1 \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
209
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir2\" uninst_file2 \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
210
+ provider.remove_package
211
+ end
212
+ end
213
+ end
214
+ end
215
+
216
+ context "installs nsis installer" do
217
+ let(:provider) { Chef::Provider::Package::Windows::Exe.new(new_resource, :nsis, uninstall_entry) }
218
+
219
+ it "calls installer with the correct flags" do
220
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"#{Regexp.quote(new_resource.source)}\" \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
221
+ provider.install_package
222
+ end
223
+ end
224
+
225
+ context "installs installshield installer" do
226
+ let(:provider) { Chef::Provider::Package::Windows::Exe.new(new_resource, :installshield, uninstall_entry) }
227
+
228
+ it "calls installer with the correct flags" do
229
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"#{Regexp.quote(new_resource.source)}\" \/s \/sms & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
230
+ provider.install_package
231
+ end
232
+ end
233
+
234
+ context "installs inno installer" do
235
+ let(:provider) { Chef::Provider::Package::Windows::Exe.new(new_resource, :inno, uninstall_entry) }
236
+
237
+ it "calls installer with the correct flags" do
238
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"#{Regexp.quote(new_resource.source)}\" \/VERYSILENT \/SUPPRESSMSGBOXES \/NORESTART & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
239
+ provider.install_package
240
+ end
241
+ end
242
+
243
+ context "installs wise installer" do
244
+ let(:provider) { Chef::Provider::Package::Windows::Exe.new(new_resource, :wise, uninstall_entry) }
245
+
246
+ it "calls installer with the correct flags" do
247
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"#{Regexp.quote(new_resource.source)}\" \/s & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
248
+ provider.install_package
249
+ end
250
+ end
251
+ end
@@ -17,17 +17,37 @@
17
17
  #
18
18
 
19
19
  require 'spec_helper'
20
+ require 'chef/provider/package/windows/msi'
20
21
 
21
22
  describe Chef::Provider::Package::Windows::MSI do
22
23
  let(:node) { double('Chef::Node') }
23
24
  let(:events) { double('Chef::Events').as_null_object } # mock all the methods
24
25
  let(:run_context) { double('Chef::RunContext', :node => node, :events => events) }
25
- let(:new_resource) { Chef::Resource::WindowsPackage.new("calculator.msi") }
26
- let(:provider) { Chef::Provider::Package::Windows::MSI.new(new_resource) }
27
-
28
- before(:each) do
29
- stub_const("File::ALT_SEPARATOR", "\\")
30
- allow(::File).to receive(:absolute_path).with("calculator.msi").and_return("calculator.msi")
26
+ let(:package_name) { "calculator" }
27
+ let(:resource_source) { "calculator.msi" }
28
+ let(:resource_version) { nil }
29
+ let(:new_resource) do
30
+ new_resource = Chef::Resource::WindowsPackage.new(package_name)
31
+ new_resource.source(resource_source)
32
+ new_resource.version(resource_version)
33
+ new_resource
34
+ end
35
+ let(:uninstall_hash) do
36
+ [{
37
+ 'DisplayVersion' => 'outdated',
38
+ 'UninstallString' => "MsiExec.exe /X{guid}"
39
+ }]
40
+ end
41
+ let(:uninstall_entry) do
42
+ entries = []
43
+ uninstall_hash.each do |entry|
44
+ entries.push(Chef::Provider::Package::Windows::RegistryUninstallEntry.new('hive', 'key', entry))
45
+ end
46
+ entries
47
+ end
48
+ let(:provider) { Chef::Provider::Package::Windows::MSI.new(new_resource, uninstall_entry) }
49
+ before do
50
+ allow(::File).to receive(:exist?).with(Chef::Util::PathHelper.canonical_path(resource_source, false)).and_return(true)
31
51
  end
32
52
 
33
53
  it "responds to shell_out!" do
@@ -50,6 +70,11 @@ describe Chef::Provider::Package::Windows::MSI do
50
70
  allow(provider).to receive(:get_installed_version).with("{23170F69-40C1-2702-0920-000001000000}").and_return("3.14159.1337.42")
51
71
  expect(provider.installed_version).to eql("3.14159.1337.42")
52
72
  end
73
+
74
+ it "returns the installed version in the registry when install file not present" do
75
+ allow(::File).to receive(:exist?).with(Chef::Util::PathHelper.canonical_path(resource_source, false)).and_return(false)
76
+ expect(provider.installed_version).to eql(["outdated"])
77
+ end
53
78
  end
54
79
 
55
80
  describe "package_version" do
@@ -57,19 +82,78 @@ describe Chef::Provider::Package::Windows::MSI do
57
82
  allow(provider).to receive(:get_product_property).with(/calculator.msi$/, "ProductVersion").and_return(42)
58
83
  expect(provider.package_version).to eql(42)
59
84
  end
85
+
86
+ context "version is explicitly provided" do
87
+ let(:resource_version) { "given_version" }
88
+
89
+ it "returns the given version" do
90
+ expect(provider.package_version).to eql("given_version")
91
+ end
92
+ end
93
+
94
+ context "no source or version is given" do
95
+ before do
96
+ allow(::File).to receive(:exist?).with(Chef::Util::PathHelper.canonical_path(resource_source, false)).and_return(false)
97
+ end
98
+
99
+ it "returns nil" do
100
+ expect(provider.package_version).to eql(nil)
101
+ end
102
+ end
60
103
  end
61
104
 
62
105
  describe "install_package" do
63
106
  it "calls msiexec /qn /i" do
64
- expect(provider).to receive(:shell_out!).with(/msiexec \/qn \/i \"calculator.msi\"/, kind_of(Hash))
65
- provider.install_package("unused", "unused")
107
+ expect(provider).to receive(:shell_out!).with(/msiexec \/qn \/i \"#{Regexp.quote(new_resource.source)}\"/, kind_of(Hash))
108
+ provider.install_package
66
109
  end
67
110
  end
68
111
 
69
112
  describe "remove_package" do
70
113
  it "calls msiexec /qn /x" do
71
- expect(provider).to receive(:shell_out!).with(/msiexec \/qn \/x \"calculator.msi\"/, kind_of(Hash))
72
- provider.remove_package("unused", "unused")
114
+ expect(provider).to receive(:shell_out!).with(/msiexec \/qn \/x \"#{Regexp.quote(new_resource.source)}\"/, kind_of(Hash))
115
+ provider.remove_package
116
+ end
117
+
118
+ context "no source is provided" do
119
+ before do
120
+ allow(::File).to receive(:exist?).with(Chef::Util::PathHelper.canonical_path(resource_source, false)).and_return(false)
121
+ end
122
+
123
+ it "removes installed package" do
124
+ expect(provider).to receive(:shell_out!).with(/MsiExec.exe \/X{guid} \/Q/, kind_of(Hash))
125
+ provider.remove_package
126
+ end
127
+
128
+ context "there are multiple installs" do
129
+ let(:uninstall_hash) do
130
+ [
131
+ {
132
+ 'DisplayVersion' => 'outdated',
133
+ 'UninstallString' => "MsiExec.exe /X{guid}"
134
+ },
135
+ {
136
+ 'DisplayVersion' => 'really_outdated',
137
+ 'UninstallString' => "MsiExec.exe /X{guid2}"
138
+ }
139
+ ]
140
+ end
141
+
142
+ it "removes both installed package" do
143
+ expect(provider).to receive(:shell_out!).with(/MsiExec.exe \/X{guid} \/Q/, kind_of(Hash))
144
+ expect(provider).to receive(:shell_out!).with(/MsiExec.exe \/X{guid2} \/Q/, kind_of(Hash))
145
+ provider.remove_package
146
+ end
147
+ end
148
+
149
+ context "custom options includes /Q" do
150
+ before { new_resource.options("/Q") }
151
+
152
+ it "does not duplicate quiet switch" do
153
+ expect(provider).to receive(:shell_out!).with(/MsiExec.exe \/X{guid} \/Q/, kind_of(Hash))
154
+ provider.remove_package
155
+ end
156
+ end
73
157
  end
74
158
  end
75
159
  end
@@ -17,6 +17,8 @@
17
17
  #
18
18
 
19
19
  require 'spec_helper'
20
+ require 'chef/provider/package/windows/exe'
21
+ require 'chef/provider/package/windows/msi'
20
22
 
21
23
  describe Chef::Provider::Package::Windows, :windows_only do
22
24
  before(:each) do
@@ -28,10 +30,19 @@ describe Chef::Provider::Package::Windows, :windows_only do
28
30
  let(:events) { double('Chef::Events').as_null_object } # mock all the methods
29
31
  let(:run_context) { double('Chef::RunContext', :node => node, :events => events) }
30
32
  let(:resource_source) { 'calculator.msi' }
31
- let(:new_resource) { Chef::Resource::WindowsPackage.new(resource_source) }
33
+ let(:resource_name) { 'calculator' }
34
+ let(:new_resource) do
35
+ new_resource = Chef::Resource::WindowsPackage.new(resource_name)
36
+ new_resource.source(resource_source)
37
+ new_resource
38
+ end
32
39
  let(:provider) { Chef::Provider::Package::Windows.new(new_resource, run_context) }
33
40
  let(:cache_path) { 'c:\\cache\\' }
34
41
 
42
+ before(:each) do
43
+ allow(::File).to receive(:exist?).with(provider.new_resource.source).and_return(true)
44
+ end
45
+
35
46
  describe "load_current_resource" do
36
47
  shared_examples "a local file" do
37
48
  before(:each) do
@@ -43,7 +54,7 @@ describe Chef::Provider::Package::Windows, :windows_only do
43
54
  it "creates a current resource with the name of the new resource" do
44
55
  provider.load_current_resource
45
56
  expect(provider.current_resource).to be_a(Chef::Resource::WindowsPackage)
46
- expect(provider.current_resource.name).to eql(resource_source)
57
+ expect(provider.current_resource.name).to eql(resource_name)
47
58
  end
48
59
 
49
60
  it "sets the current version if the package is installed" do
@@ -76,19 +87,6 @@ describe Chef::Provider::Package::Windows, :windows_only do
76
87
  end
77
88
  it_behaves_like "a local file"
78
89
  end
79
-
80
- context "when remote_file_attributes are provided" do
81
- let (:remote_file_attributes) { {:path => 'C:\\foobar.msi'} }
82
- before(:each) do
83
- new_resource.remote_file_attributes(remote_file_attributes)
84
- end
85
-
86
- it 'should override the attributes of the remote file resource used' do
87
- expect(::File).to receive(:exists?).with(remote_file_attributes[:path])
88
- provider.load_current_resource
89
- end
90
-
91
- end
92
90
  end
93
91
 
94
92
  context "when source is a local file" do
@@ -98,6 +96,7 @@ describe Chef::Provider::Package::Windows, :windows_only do
98
96
 
99
97
  describe "package_provider" do
100
98
  shared_examples "a local file" do
99
+
101
100
  it "checks that the source path is valid" do
102
101
  expect(Chef::Util::PathHelper).to receive(:validate_path)
103
102
  provider.package_provider
@@ -108,9 +107,29 @@ describe Chef::Provider::Package::Windows, :windows_only do
108
107
  expect(provider.package_provider).to be_a(Chef::Provider::Package::Windows::MSI)
109
108
  end
110
109
 
111
- it "raises an error if the installer_type is unknown" do
112
- allow(provider).to receive(:installer_type).and_return(:apt_for_windows)
113
- expect { provider.package_provider }.to raise_error
110
+ it "sets the package provider to Exe if the the installer type is :inno" do
111
+ allow(provider).to receive(:installer_type).and_return(:inno)
112
+ expect(provider.package_provider).to be_a(Chef::Provider::Package::Windows::Exe)
113
+ end
114
+
115
+ it "sets the package provider to Exe if the the installer type is :nsis" do
116
+ allow(provider).to receive(:installer_type).and_return(:nsis)
117
+ expect(provider.package_provider).to be_a(Chef::Provider::Package::Windows::Exe)
118
+ end
119
+
120
+ it "sets the package provider to Exe if the the installer type is :wise" do
121
+ allow(provider).to receive(:installer_type).and_return(:wise)
122
+ expect(provider.package_provider).to be_a(Chef::Provider::Package::Windows::Exe)
123
+ end
124
+
125
+ it "sets the package provider to Exe if the the installer type is :installshield" do
126
+ allow(provider).to receive(:installer_type).and_return(:installshield)
127
+ expect(provider.package_provider).to be_a(Chef::Provider::Package::Windows::Exe)
128
+ end
129
+
130
+ it "defaults to exe if the installer_type is unknown" do
131
+ allow(provider).to receive(:installer_type).and_return(nil)
132
+ expect(provider.package_provider).to be_a(Chef::Provider::Package::Windows::Exe)
114
133
  end
115
134
  end
116
135
 
@@ -146,20 +165,202 @@ describe Chef::Provider::Package::Windows, :windows_only do
146
165
  end
147
166
 
148
167
  describe "installer_type" do
149
- it "it returns @installer_type if it is set" do
168
+ let(:resource_source) { "microsoft_installer.exe" }
169
+
170
+ context "there is no source" do
171
+ let(:uninstall_hash) do
172
+ [{
173
+ 'DisplayVersion' => 'outdated',
174
+ 'UninstallString' => "blah blah"
175
+ }]
176
+ end
177
+ let(:uninstall_key) { "blah" }
178
+ let(:uninstall_entry) do
179
+ entries = []
180
+ uninstall_hash.each do |entry|
181
+ entries.push(Chef::Provider::Package::Windows::RegistryUninstallEntry.new('hive', uninstall_key, entry))
182
+ end
183
+ entries
184
+ end
185
+
186
+ before do
187
+ allow(Chef::Provider::Package::Windows::RegistryUninstallEntry).to receive(:find_entries).and_return(uninstall_entry)
188
+ allow(::File).to receive(:exist?).with(Chef::Util::PathHelper.canonical_path(resource_source, false)).and_return(false)
189
+ end
190
+
191
+ context "uninstall string contains MsiExec.exe" do
192
+ let(:uninstall_hash) do
193
+ [{
194
+ 'DisplayVersion' => 'outdated',
195
+ 'UninstallString' => "MsiExec.exe /X{guid}"
196
+ }]
197
+ end
198
+
199
+ it "sets installer_type to MSI" do
200
+ expect(provider.installer_type).to eql(:msi)
201
+ end
202
+ end
203
+
204
+ context "uninstall string ends with uninst.exe" do
205
+ let(:uninstall_hash) do
206
+ [{
207
+ 'DisplayVersion' => 'outdated',
208
+ 'UninstallString' => %q{"c:/hfhfheru/uninst.exe"}
209
+ }]
210
+ end
211
+
212
+ it "sets installer_type to NSIS" do
213
+ expect(provider.installer_type).to eql(:nsis)
214
+ end
215
+ end
216
+
217
+ context "uninstall key ends in _is1" do
218
+ let(:uninstall_key) { "blah_is1" }
219
+
220
+ it "sets installer_type to inno" do
221
+ expect(provider.installer_type).to eql(:inno)
222
+ end
223
+ end
224
+
225
+ context "eninstall entries is empty" do
226
+ before { allow(Chef::Provider::Package::Windows::RegistryUninstallEntry).to receive(:find_entries).and_return([]) }
227
+
228
+ it "returns nil" do
229
+ expect(provider.installer_type).to eql(nil)
230
+ end
231
+ end
232
+ end
233
+
234
+ it "returns @installer_type if it is set" do
150
235
  provider.new_resource.installer_type(:downeaster)
151
236
  expect(provider.installer_type).to eql(:downeaster)
152
237
  end
153
238
 
154
- it "sets installer_type to msi if the source ends in .msi" do
155
- provider.new_resource.source("microsoft_installer.msi")
156
- expect(provider.installer_type).to eql(:msi)
239
+ it "sets installer_type to inno if the source contains inno" do
240
+ allow(::Kernel).to receive(:open).and_yield(StringIO.new('blah blah inno blah'))
241
+ expect(provider.installer_type).to eql(:inno)
157
242
  end
158
243
 
159
- it "raises an error if it cannot determine the installer type" do
160
- provider.new_resource.installer_type(nil)
161
- provider.new_resource.source("tomfoolery.now")
162
- expect { provider.installer_type }.to raise_error(ArgumentError)
244
+ it "sets installer_type to wise if the source contains wise" do
245
+ allow(::Kernel).to receive(:open).and_yield(StringIO.new('blah blah wise blah'))
246
+ expect(provider.installer_type).to eql(:wise)
247
+ end
248
+
249
+ it "sets installer_type to nsis if the source contains nsis" do
250
+ allow(::Kernel).to receive(:open).and_yield(StringIO.new('blah blah nullsoft blah'))
251
+ expect(provider.installer_type).to eql(:nsis)
252
+ end
253
+
254
+ context "source ends in .msi" do
255
+ let(:resource_source) { "microsoft_installer.msi" }
256
+
257
+ it "sets installer_type to msi" do
258
+ expect(provider.installer_type).to eql(:msi)
259
+ end
260
+ end
261
+
262
+ context "the source is setup.exe" do
263
+ let(:resource_source) { "setup.exe" }
264
+
265
+ it "sets installer_type to installshield" do
266
+ allow(::Kernel).to receive(:open).and_yield(StringIO.new(''))
267
+ expect(provider.installer_type).to eql(:installshield)
268
+ end
269
+ end
270
+
271
+ context "cannot determine the installer type" do
272
+ let(:resource_source) { "tomfoolery.now" }
273
+
274
+ it "raises an error" do
275
+ allow(::Kernel).to receive(:open).and_yield(StringIO.new(''))
276
+ provider.new_resource.installer_type(nil)
277
+ expect { provider.installer_type }.to raise_error(Chef::Exceptions::CannotDetermineWindowsInstallerType)
278
+ end
279
+ end
280
+ end
281
+
282
+ describe "action_install" do
283
+ let(:new_resource) { Chef::Resource::WindowsPackage.new("blah.exe") }
284
+ before do
285
+ new_resource.installer_type(:inno)
286
+ allow_any_instance_of(Chef::Provider::Package::Windows::Exe).to receive(:package_version).and_return(new_resource.version)
287
+ end
288
+
289
+ context "no version given, discovered or installed" do
290
+ it "installs latest" do
291
+ expect(provider).to receive(:install_package).with("blah.exe", "latest")
292
+ provider.run_action(:install)
293
+ end
294
+ end
295
+
296
+ context "no version given or discovered but package is installed" do
297
+ before { allow(provider).to receive(:current_version_array).and_return(["5.5.5"]) }
298
+
299
+ it "does not install" do
300
+ expect(provider).not_to receive(:install_package)
301
+ provider.run_action(:install)
302
+ end
303
+ end
304
+
305
+ context "a version is given and none is installed" do
306
+ before { new_resource.version('5.5.5') }
307
+
308
+ it "installs given version" do
309
+ expect(provider).to receive(:install_package).with("blah.exe", "5.5.5")
310
+ provider.run_action(:install)
311
+ end
312
+ end
313
+
314
+ context "a version is given and several are installed" do
315
+ context "given version matches an installed version" do
316
+ before do
317
+ new_resource.version('5.5.5')
318
+ allow(provider).to receive(:current_version_array).and_return([ ["5.5.5", "4.3.0", "1.1.1"] ])
319
+ end
320
+
321
+ it "does not install" do
322
+ expect(provider).not_to receive(:install_package)
323
+ provider.run_action(:install)
324
+ end
325
+ end
326
+
327
+ context "given version does not match an installed version" do
328
+ before do
329
+ new_resource.version('5.5.5')
330
+ allow(provider).to receive(:current_version_array).and_return([ ["5.5.0", "4.3.0", "1.1.1"] ])
331
+ end
332
+
333
+ it "installs given version" do
334
+ expect(provider).to receive(:install_package).with("blah.exe", "5.5.5")
335
+ provider.run_action(:install)
336
+ end
337
+ end
338
+ end
339
+
340
+ context "a version is given and one is installed" do
341
+ context "given version matches installed version" do
342
+ before do
343
+ new_resource.version('5.5.5')
344
+ allow(provider).to receive(:current_version_array).and_return(["5.5.5"])
345
+ end
346
+
347
+ it "does not install" do
348
+ expect(provider).not_to receive(:install_package)
349
+ provider.run_action(:install)
350
+ end
351
+ end
352
+
353
+ context "given version does not match installed version" do
354
+ before do
355
+ new_resource.version('5.5.5')
356
+ allow(provider).to receive(:current_version_array).and_return(["5.5.0"])
357
+ end
358
+
359
+ it "installs given version" do
360
+ expect(provider).to receive(:install_package).with("blah.exe", "5.5.5")
361
+ provider.run_action(:install)
362
+ end
363
+ end
163
364
  end
164
365
  end
165
366
  end