chef 15.11.8-universal-mingw32 → 15.12.22-universal-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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -3
  3. data/README.md +3 -3
  4. data/chef.gemspec +3 -3
  5. data/distro/powershell/chef/chef.psm1 +3 -3
  6. data/distro/templates/powershell/chef/chef.psm1.erb +3 -3
  7. data/lib/chef/application/apply.rb +1 -1
  8. data/lib/chef/chef_fs/path_utils.rb +3 -3
  9. data/lib/chef/cookbook/file_system_file_vendor.rb +1 -1
  10. data/lib/chef/data_bag.rb +2 -2
  11. data/lib/chef/data_collector/error_handlers.rb +1 -1
  12. data/lib/chef/deprecated.rb +8 -0
  13. data/lib/chef/dsl/declare_resource.rb +1 -1
  14. data/lib/chef/dsl/platform_introspection.rb +2 -0
  15. data/lib/chef/knife/bootstrap.rb +3 -6
  16. data/lib/chef/knife/bootstrap/templates/chef-full.erb +9 -9
  17. data/lib/chef/provider/package/cab.rb +1 -1
  18. data/lib/chef/provider/package/chocolatey.rb +1 -1
  19. data/lib/chef/provider/package/msu.rb +1 -0
  20. data/lib/chef/provider/package/powershell.rb +5 -1
  21. data/lib/chef/provider/package/snap.rb +96 -27
  22. data/lib/chef/provider/zypper_repository.rb +30 -10
  23. data/lib/chef/resource/archive_file.rb +28 -8
  24. data/lib/chef/resource/cron_access.rb +11 -3
  25. data/lib/chef/resource/hostname.rb +2 -1
  26. data/lib/chef/resource/msu_package.rb +5 -0
  27. data/lib/chef/version.rb +1 -1
  28. data/spec/functional/resource/msu_package_spec.rb +5 -2
  29. data/spec/functional/resource/windows_task_spec.rb +8 -8
  30. data/spec/unit/application_spec.rb +7 -0
  31. data/spec/unit/data_bag_spec.rb +1 -1
  32. data/spec/unit/knife/bootstrap_spec.rb +2 -2
  33. data/spec/unit/mixin/user_context_spec.rb +1 -9
  34. data/spec/unit/property_spec.rb +1 -1
  35. data/spec/unit/provider/package/powershell_spec.rb +95 -86
  36. data/spec/unit/provider/package/snap_spec.rb +1 -1
  37. data/spec/unit/provider/zypper_repository_spec.rb +75 -25
  38. data/spec/unit/resource/archive_file_spec.rb +11 -2
  39. data/spec/unit/resource/msu_package_spec.rb +4 -0
  40. data/spec/unit/resource/windows_dns_record_spec.rb +3 -3
  41. data/spec/unit/resource/windows_dns_zone_spec.rb +2 -2
  42. data/spec/unit/resource/windows_task_spec.rb +1 -1
  43. data/spec/unit/resource/windows_uac_spec.rb +2 -2
  44. data/spec/unit/resource/yum_repository_spec.rb +21 -21
  45. data/spec/unit/resource_spec.rb +1 -1
  46. data/spec/unit/util/threaded_job_queue_spec.rb +9 -0
  47. metadata +22 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c2901561acd0b7abc61196f12812c177b5ac2a6c41e7b90b0261f86d72215cae
4
- data.tar.gz: c17d27c283cc290e915b88a8ab3d4778342e03bf05a4587e4853d9432a89ec55
3
+ metadata.gz: d7d10dc56cf8158118e798321235a2e20cd396d0c271c1d8da451394f99cd5dc
4
+ data.tar.gz: d8d9d42fdfd9afe9bff18bf6cee3774518ae3633544d7b40dc8179b4bd6aaed7
5
5
  SHA512:
6
- metadata.gz: bc360f04704971ace8f920c2c4f292cf618f197cd6203cc845f75c7f6dfa345e203937d5d603f224938ef0e91f9949f860b2a78dda5e39154d9f8f4ddc5cd096
7
- data.tar.gz: 3e982bc5c23ad7f9a393a7b7d8cdd2188e8e3f49c9f5335c50dc85065c64289207fc0989f86ffa158b1b9174b48910d22b6a2bfdd18fc96dd1173c2fed341884
6
+ metadata.gz: e7dda48107577e29a021c120368b4683a7049660d79df8a2ae45912b198d89ed0c568f80c51cd341dc35aa3801b4319ed568e66f09454f2c903bfc5d4e4100d9
7
+ data.tar.gz: ecb3de98f0e4a7c8d765a9f3bf2eebe192377120447f467ea541d9c936e284768a2f96d3ccf6fe6e3459ddcd4c66703e1f188647b02723806f3b9e0b6ef977dc
data/Gemfile CHANGED
@@ -22,8 +22,7 @@ end
22
22
 
23
23
  gem "cheffish", "~> 14"
24
24
 
25
- # avoid bringing in the new http 4 gem that comes with other ffi baggage which breaks builds
26
- gem "chef-telemetry", "=1.0.3"
25
+ gem "chef-telemetry", ">=1.0.8" # 1.0.8 removes the http dep
27
26
 
28
27
  group(:omnibus_package) do
29
28
  gem "appbundler"
@@ -39,7 +38,7 @@ group(:omnibus_package, :pry) do
39
38
  gem "pry"
40
39
  gem "pry-byebug"
41
40
  gem "pry-remote"
42
- gem "pry-stack_explorer"
41
+ gem "pry-stack_explorer", "~> 0.4.0" # pin to allow Ruby 2.5
43
42
  end
44
43
 
45
44
  group(:docgen) do
data/README.md CHANGED
@@ -18,7 +18,7 @@ Chef Infra is a configuration management tool designed to bring automation to yo
18
18
 
19
19
  ### Want to try Chef Infra?
20
20
 
21
- For Chef Infra usage, please refer to our [Learn Chef Rally](https://learn.chef.io/) website, which includes module-based training for Chef Infra, as well as Automate, Habitat, and InSpec.
21
+ For Chef Infra usage, please refer to [Learn Chef](https://learn.chef.io/), our self-paced, entirely free learning platform. Learn Chef also includes module-based training for Chef Infra, as well as Chef Automate, Chef Habitat, and Chef InSpec.
22
22
 
23
23
  Other useful resources for Chef Infra users:
24
24
 
@@ -26,7 +26,7 @@ Other useful resources for Chef Infra users:
26
26
  - Source: <https://github.com/chef/chef/tree/master>
27
27
  - Tickets/Issues: <https://github.com/chef/chef/issues>
28
28
  - Slack: [Chef Community Slack](https://community-slack.chef.io/)
29
- - Mailing list: <https://discourse.chef.io>
29
+ - Mailing list/Forum: <https://discourse.chef.io>
30
30
 
31
31
  ## Reporting Issues
32
32
 
@@ -46,7 +46,7 @@ We'd love to have your help developing Chef Infra. See our [Contributing Documen
46
46
 
47
47
  ## License and Copyright
48
48
 
49
- Copyright 2008-2019, Chef Software, Inc.
49
+ Copyright 2008-2020, Chef Software, Inc.
50
50
 
51
51
  ```
52
52
  Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,16 +30,16 @@ Gem::Specification.new do |s|
30
30
 
31
31
  s.add_dependency "ffi", "~> 1.9", ">= 1.9.25"
32
32
  s.add_dependency "ffi-yajl", "~> 2.2"
33
- s.add_dependency "net-ssh", ">= 4.2", "< 6"
33
+ s.add_dependency "net-ssh", ">= 4.2", "< 7"
34
34
  s.add_dependency "net-ssh-multi", "~> 1.2", ">= 1.2.1"
35
- s.add_dependency "net-sftp", "~> 2.1", ">= 2.1.2"
35
+ s.add_dependency "net-sftp", ">= 2.1.2", "< 4.0"
36
36
  s.add_dependency "ed25519", "~> 1.2" # ed25519 ssh key support
37
37
  s.add_dependency "bcrypt_pbkdf", "~> 1.0" # ed25519 ssh key support
38
38
  s.add_dependency "highline", ">= 1.6.9", "< 2"
39
39
  s.add_dependency "tty-screen", "~> 0.6" # knife list
40
40
  s.add_dependency "erubis", "~> 2.7"
41
41
  s.add_dependency "diff-lcs", "~> 1.2", ">= 1.2.4"
42
- s.add_dependency "ffi-libarchive"
42
+ s.add_dependency "ffi-libarchive", "~> 1.0", ">= 1.0.3"
43
43
  s.add_dependency "chef-zero", ">= 14.0.11"
44
44
 
45
45
  s.add_dependency "plist", "~> 3.2"
@@ -358,7 +358,7 @@ function Run-RubyCommand($command, $argList) {
358
358
  # When arguments come into this method, the standard PS rules for interpreting cmdlet arguments
359
359
  # apply. When using & (call operator) and providing an array of arguments, powershell (verified
360
360
  # on PS 4.0 on Windows Server 2012R2) will not evaluate them but (contrary to documentation),
361
- # it will still marginally interpret them. The behaviour of PS 5.0 seems to be different but
361
+ # it will still marginally interpret them. The behavior of PS 5.0 seems to be different but
362
362
  # ignore that for now. If any of the provided arguments has a space in it, powershell checks
363
363
  # the first and last character to ensure that they are " characters (and that's all it checks).
364
364
  # If they are not, it will blindly surround that argument with " characters. It won't do this
@@ -381,10 +381,10 @@ function Run-RubyCommand($command, $argList) {
381
381
  # Command line:
382
382
  # "C:\Program Files (x86)\PowerShell Community Extensions\Pscx3\Pscx\Apps\EchoArgs.exe" "foo '' bar "baz"" "foo '' bar "baz""
383
383
  #
384
- # $x = "abc'123'nospace`"lulz`"!!!"
384
+ # $x = "abc'123'nospace`"lol`"!!!"
385
385
  # & EchoArgs @($x, $x)
386
386
  # Command line:
387
- # "C:\Program Files (x86)\PowerShell Community Extensions\Pscx3\Pscx\Apps\EchoArgs.exe" abc'123'nospace"lulz"!!! abc'123'nospace"lulz"!!!
387
+ # "C:\Program Files (x86)\PowerShell Community Extensions\Pscx3\Pscx\Apps\EchoArgs.exe" abc'123'nospace"lol"!!! abc'123'nospace"lol"!!!
388
388
  #
389
389
  # $x = "`"`"Look ma! Tonnes of spaces! 'foo' 'bar'`"`""
390
390
  # & EchoArgs @($x, $x)
@@ -358,7 +358,7 @@ function Run-RubyCommand($command, $argList) {
358
358
  # When arguments come into this method, the standard PS rules for interpreting cmdlet arguments
359
359
  # apply. When using & (call operator) and providing an array of arguments, powershell (verified
360
360
  # on PS 4.0 on Windows Server 2012R2) will not evaluate them but (contrary to documentation),
361
- # it will still marginally interpret them. The behaviour of PS 5.0 seems to be different but
361
+ # it will still marginally interpret them. The behavior of PS 5.0 seems to be different but
362
362
  # ignore that for now. If any of the provided arguments has a space in it, powershell checks
363
363
  # the first and last character to ensure that they are " characters (and that's all it checks).
364
364
  # If they are not, it will blindly surround that argument with " characters. It won't do this
@@ -381,10 +381,10 @@ function Run-RubyCommand($command, $argList) {
381
381
  # Command line:
382
382
  # "C:\Program Files (x86)\PowerShell Community Extensions\Pscx3\Pscx\Apps\EchoArgs.exe" "foo '' bar "baz"" "foo '' bar "baz""
383
383
  #
384
- # $x = "abc'123'nospace`"lulz`"!!!"
384
+ # $x = "abc'123'nospace`"lol`"!!!"
385
385
  # & EchoArgs @($x, $x)
386
386
  # Command line:
387
- # "C:\Program Files (x86)\PowerShell Community Extensions\Pscx3\Pscx\Apps\EchoArgs.exe" abc'123'nospace"lulz"!!! abc'123'nospace"lulz"!!!
387
+ # "C:\Program Files (x86)\PowerShell Community Extensions\Pscx3\Pscx\Apps\EchoArgs.exe" abc'123'nospace"lol"!!! abc'123'nospace"lol"!!!
388
388
  #
389
389
  # $x = "`"`"Look ma! Tonnes of spaces! 'foo' 'bar'`"`""
390
390
  # & EchoArgs @($x, $x)
@@ -216,7 +216,7 @@ class Chef::Application::Apply < Chef::Application
216
216
  end
217
217
 
218
218
  # Get this party started
219
- def run(enforce_license = false)
219
+ def run(enforce_license: false)
220
220
  reconfigure
221
221
  check_license_acceptance if enforce_license
222
222
  run_application
@@ -71,9 +71,9 @@ class Chef
71
71
  # part that actually exists. The paths operated on here are not Chef-FS paths.
72
72
  # These are OS paths that may contain symlinks but may not also fully exist.
73
73
  #
74
- # If /x is a symlink to /blarghle, and has no subdirectories, then:
75
- # PathUtils.realest_path('/x/y/z') == '/blarghle/y/z'
76
- # PathUtils.realest_path('/x/*/z') == '/blarghle/*/z'
74
+ # If /x is a symlink to /foo_bar, and has no subdirectories, then:
75
+ # PathUtils.realest_path('/x/y/z') == '/foo_bar/y/z'
76
+ # PathUtils.realest_path('/x/*/z') == '/foo_bar/*/z'
77
77
  # PathUtils.realest_path('/*/y/z') == '/*/y/z'
78
78
  #
79
79
  # TODO: Move this to wherever util/path_helper is these days.
@@ -27,7 +27,7 @@ class Chef
27
27
  # and throws the rest away then re-builds the list of files on the
28
28
  # disk. This is due to the manifest not having the on-disk file
29
29
  # locations, since in the chef-client case, that information is
30
- # non-sensical.
30
+ # nonsensical.
31
31
  class FileSystemFileVendor < FileVendor
32
32
 
33
33
  attr_reader :cookbook_name
@@ -94,7 +94,7 @@ class Chef
94
94
  names = []
95
95
  paths.each do |path|
96
96
  unless File.directory?(path)
97
- raise Chef::Exceptions::InvalidDataBagPath, "Data bag path '#{path}' is invalid"
97
+ raise Chef::Exceptions::InvalidDataBagPath, "Data bag path '#{path}' not found. Please create this directory."
98
98
  end
99
99
 
100
100
  names += Dir.glob(File.join(
@@ -122,7 +122,7 @@ class Chef
122
122
  data_bag = {}
123
123
  paths.each do |path|
124
124
  unless File.directory?(path)
125
- raise Chef::Exceptions::InvalidDataBagPath, "Data bag path '#{path}' is invalid"
125
+ raise Chef::Exceptions::InvalidDataBagPath, "Data bag path '#{path}' not found. Please create this directory."
126
126
  end
127
127
 
128
128
  Dir.glob(File.join(Chef::Util::PathHelper.escape_glob_dir(path, name.to_s), "*.json")).inject({}) do |bag, f|
@@ -18,7 +18,7 @@
18
18
  class Chef
19
19
  class DataCollector
20
20
 
21
- # This module isolates the handling of collecting error descriptions to insert into the data_colletor
21
+ # This module isolates the handling of collecting error descriptions to insert into the data_collector
22
22
  # report output. For very early errors it is responsible for collecting the node_name for the report
23
23
  # to use. For all failure conditions that have an ErrorMapper it collects the output.
24
24
  #
@@ -233,6 +233,14 @@ class Chef
233
233
  target 28
234
234
  end
235
235
 
236
+ class KnifeBootstrapApis < Base
237
+ target 29
238
+ end
239
+
240
+ class ArchiveFileIntegerFileMode < Base
241
+ target 30
242
+ end
243
+
236
244
  class Generic < Base
237
245
  def url
238
246
  "https://docs.chef.io/chef_deprecations_client/"
@@ -151,7 +151,7 @@ class Chef
151
151
  # source "y.txt.erb"
152
152
  # variables {}
153
153
  # end
154
- # resource.variables.merge!({ home: "/home/klowns" })
154
+ # resource.variables.merge!({ home: "/home/clowns" })
155
155
  #
156
156
  def edit_resource(type, name, created_at: nil, run_context: self.run_context, &resource_attrs_block)
157
157
  edit_resource!(type, name, created_at: created_at, run_context: run_context, &resource_attrs_block)
@@ -248,6 +248,8 @@ class Chef
248
248
  end
249
249
 
250
250
  # a simple helper to determine if we're on a windows release pre-2012 / 8
251
+ #
252
+ # @deprecated Windows releases before Windows 2012 and 8 are no longer supported
251
253
  # @return [Boolean] Is the system older than Windows 8 / 2012
252
254
  def older_than_win_2012_or_8?(node = run_context.nil? ? nil : run_context.node)
253
255
  node["platform_version"].to_f < 6.2
@@ -597,11 +597,8 @@ class Chef
597
597
 
598
598
  bootstrap_context.client_pem = client_builder.client_path
599
599
  else
600
- ui.info <<~EOM
601
- Performing legacy client registration with the validation key at #{Chef::Config[:validation_key]}...
602
- Delete your validation key in order to use your user credentials for client registration instead.
603
- EOM
604
-
600
+ ui.warn "Performing legacy client registration with the validation key at #{Chef::Config[:validation_key]}..."
601
+ ui.warn "Remove the key file or remove the 'validation_key' configuration option from your config.rb (knife.rb) to use more secure user credentials for client registration."
605
602
  end
606
603
  end
607
604
 
@@ -619,7 +616,7 @@ class Chef
619
616
  end
620
617
 
621
618
  def connect!
622
- ui.info("Connecting to #{ui.color(server_name, :bold)}")
619
+ ui.info("Connecting to #{ui.color(server_name, :bold)} using #{connection_protocol}")
623
620
  opts ||= connection_opts.dup
624
621
  do_connect(opts)
625
622
  rescue Train::Error => e
@@ -185,50 +185,50 @@ if test "x$tmp_dir" != "x"; then
185
185
  rm -r "$tmp_dir"
186
186
  fi
187
187
 
188
- mkdir -p <%= ChefConfig::Config.etc_chef_dir(false) %>
188
+ mkdir -p /etc/chef
189
189
 
190
190
  <% if client_pem -%>
191
- (umask 077 && (cat > <%= ChefConfig::Config.etc_chef_dir(false) %>/client.pem <<'EOP'
191
+ (umask 077 && (cat > /etc/chef/client.pem <<'EOP'
192
192
  <%= ::File.read(::File.expand_path(client_pem)) %>
193
193
  EOP
194
194
  )) || exit 1
195
195
  <% end -%>
196
196
 
197
197
  <% if validation_key -%>
198
- (umask 077 && (cat > <%= ChefConfig::Config.etc_chef_dir(false) %>/validation.pem <<'EOP'
198
+ (umask 077 && (cat > /etc/chef/validation.pem <<'EOP'
199
199
  <%= validation_key %>
200
200
  EOP
201
201
  )) || exit 1
202
202
  <% end -%>
203
203
 
204
204
  <% if encrypted_data_bag_secret -%>
205
- (umask 077 && (cat > <%= ChefConfig::Config.etc_chef_dir(false) %>/encrypted_data_bag_secret <<'EOP'
205
+ (umask 077 && (cat > /etc/chef/encrypted_data_bag_secret <<'EOP'
206
206
  <%= encrypted_data_bag_secret %>
207
207
  EOP
208
208
  )) || exit 1
209
209
  <% end -%>
210
210
 
211
211
  <% unless trusted_certs.empty? -%>
212
- mkdir -p <%= ChefConfig::Config.etc_chef_dir(false) %>/trusted_certs
212
+ mkdir -p /etc/chef/trusted_certs
213
213
  <%= trusted_certs %>
214
214
  <% end -%>
215
215
 
216
216
  <%# Generate Ohai Hints -%>
217
217
  <% unless @chef_config[:knife][:hints].nil? || @chef_config[:knife][:hints].empty? -%>
218
- mkdir -p <%= ChefConfig::Config.etc_chef_dir(false) %>/ohai/hints
218
+ mkdir -p /etc/chef/ohai/hints
219
219
 
220
220
  <% @chef_config[:knife][:hints].each do |name, hash| -%>
221
- cat > <%= ChefConfig::Config.etc_chef_dir(false) %>/ohai/hints/<%= name %>.json <<'EOP'
221
+ cat > /etc/chef/ohai/hints/<%= name %>.json <<'EOP'
222
222
  <%= Chef::JSONCompat.to_json(hash) %>
223
223
  EOP
224
224
  <% end -%>
225
225
  <% end -%>
226
226
 
227
- cat > <%= ChefConfig::Config.etc_chef_dir(false) %>/client.rb <<'EOP'
227
+ cat > /etc/chef/client.rb <<'EOP'
228
228
  <%= config_content %>
229
229
  EOP
230
230
 
231
- cat > <%= ChefConfig::Config.etc_chef_dir(false) %>/first-boot.json <<'EOP'
231
+ cat > /etc/chef/first-boot.json <<'EOP'
232
232
  <%= Chef::JSONCompat.to_json(first_boot) %>
233
233
  EOP
234
234
 
@@ -94,7 +94,7 @@ class Chef
94
94
  split_package_identity(p["package_identity"])
95
95
  end
96
96
  found_packages = existing_package_identities.select do |existing_package_ident|
97
- existing_package_ident["name"] == package["name"]
97
+ existing_package_ident["version"] == package["version"].chomp && existing_package_ident["name"] == package["name"]
98
98
  end
99
99
  if found_packages.empty?
100
100
  nil
@@ -251,7 +251,7 @@ class Chef
251
251
  end
252
252
 
253
253
  # Helper to convert choco.exe list output to a Hash
254
- # (names are downcased for case-insenstive matching)
254
+ # (names are downcased for case-insensitive matching)
255
255
  #
256
256
  # @param cmd [String] command to run
257
257
  # @return [Hash] list output converted to ruby Hash
@@ -109,6 +109,7 @@ class Chef
109
109
  @cab_files.each do |cab_file|
110
110
  declare_resource(:cab_package, new_resource.name) do
111
111
  source cab_file
112
+ timeout new_resource.timeout
112
113
  action :install
113
114
  end
114
115
  end
@@ -53,6 +53,9 @@ class Chef
53
53
 
54
54
  # Installs the package specified with the version passed else latest version will be installed
55
55
  def install_package(names, versions)
56
+ # To enable tls 1.2, which is disabled by default in some OS
57
+ powershell_out("[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12")
58
+
56
59
  names.each_with_index do |name, index|
57
60
  cmd = powershell_out(build_powershell_package_command("Install-Package '#{name}'", versions[index]), timeout: new_resource.timeout)
58
61
  next if cmd.nil?
@@ -115,7 +118,8 @@ class Chef
115
118
  command = [command] unless command.is_a?(Array)
116
119
  cmdlet_name = command.first
117
120
  command.unshift("(")
118
- %w{-Force -ForceBootstrap}.each do |arg|
121
+ # -WarningAction SilentlyContinue is used to suppress the warnings from stdout
122
+ %w{-Force -ForceBootstrap -WarningAction SilentlyContinue}.each do |arg|
119
123
  command.push(arg)
120
124
  end
121
125
  command.push("-RequiredVersion #{version}") if version
@@ -59,15 +59,14 @@ class Chef
59
59
  def get_current_versions
60
60
  package_name_array.each_with_index.map do |pkg, i|
61
61
  installed_version(i)
62
- end
62
+ end.compact
63
63
  end
64
64
 
65
65
  def install_package(names, versions)
66
66
  if new_resource.source
67
67
  install_snap_from_source(names, new_resource.source)
68
68
  else
69
- resolved_names = names.each_with_index.map { |name, i| available_version(i).to_s unless name.nil? }
70
- install_snaps(resolved_names)
69
+ install_snaps(names)
71
70
  end
72
71
  end
73
72
 
@@ -75,14 +74,16 @@ class Chef
75
74
  if new_resource.source
76
75
  install_snap_from_source(names, new_resource.source)
77
76
  else
78
- resolved_names = names.each_with_index.map { |name, i| available_version(i).to_s unless name.nil? }
79
- update_snaps(resolved_names)
77
+ if get_current_versions.empty?
78
+ install_snaps(names, versions)
79
+ else
80
+ update_snaps(names)
81
+ end
80
82
  end
81
83
  end
82
84
 
83
85
  def remove_package(names, versions)
84
- resolved_names = names.each_with_index.map { |name, i| installed_version(i).to_s unless name.nil? }
85
- uninstall_snaps(resolved_names)
86
+ uninstall_snaps(names)
86
87
  end
87
88
 
88
89
  alias purge_package remove_package
@@ -129,19 +130,73 @@ class Chef
129
130
  "Accept: application/json\r\n" +
130
131
  "Content-Type: application/json\r\n"
131
132
  if method == "POST"
132
- request.concat("Content-Length: #{post_data.bytesize}\r\n\r\n#{post_data}")
133
+ pdata = post_data.to_json.to_s
134
+ request.concat("Content-Length: #{pdata.bytesize}\r\n\r\n#{pdata}")
133
135
  end
134
136
  request.concat("\r\n")
135
- # While it is expected to allow clients to connect using HTTPS over a TCP socket,
136
- # at this point only a UNIX socket is supported. The socket is /run/snapd.socket
137
- # Note - UNIXSocket is not defined on windows systems
137
+
138
+ # while it is expected to allow clients to connect using https over
139
+ # a tcp socket, at this point only a unix socket is supported. the
140
+ # socket is /run/snapd.socket note - unixsocket is not defined on
141
+ # windows systems
138
142
  if defined?(::UNIXSocket)
139
143
  UNIXSocket.open("/run/snapd.socket") do |socket|
140
- # Send request, read the response, split the response and parse the body
141
- socket.print(request)
142
- response = socket.read
143
- headers, body = response.split("\r\n\r\n", 2)
144
- JSON.parse(body)
144
+ # send request, read the response, split the response and parse
145
+ # the body
146
+ socket.write(request)
147
+
148
+ # WARNING!!! HERE BE DRAGONs
149
+ #
150
+ # So snapd doesn't return an EOF at the end of its body, so
151
+ # doing a normal read will just hang forever.
152
+ #
153
+ # Well, sort of. if, after it writes everything, you then send
154
+ # yet-another newline, it'll then send its EOF and promptly
155
+ # disconnect closing the pipe and preventing reading. so, you
156
+ # have to read first, and therein lies the EOF problem.
157
+ #
158
+ # So you can do non-blocking reads with selects, but it
159
+ # makes every read take about 5 seconds. If, instead, we
160
+ # read the last line char-by-char, it's about half a second.
161
+ #
162
+ # Reading a character at a time isn't efficient, and since we
163
+ # know that http headers always have a blank line after them,
164
+ # we can read lines until we find a blank line and *then* read
165
+ # a character at a time. snap returns all the json on a single
166
+ # line, so once you pass headers you must read a character a
167
+ # time.
168
+ #
169
+ # - jaymzh
170
+
171
+ Chef::Log.trace(
172
+ "snap_package[#{new_resource.package_name}]: reading headers"
173
+ )
174
+ loop do
175
+ response = socket.readline
176
+ break if response.strip.empty? # finished headers
177
+ end
178
+ Chef::Log.trace(
179
+ "snap_package[#{new_resource.package_name}]: past headers, " +
180
+ "onto the body..."
181
+ )
182
+ result = nil
183
+ body = ""
184
+ socket.each_char do |c|
185
+ body << c
186
+ # we know we're not done if we don't have a char that
187
+ # can end JSON
188
+ next unless ["}", "]"].include?(c)
189
+
190
+ begin
191
+ result = JSON.parse(body)
192
+ # if we get here, we were able to parse the json so we
193
+ # are done reading
194
+ break
195
+ rescue JSON::ParserError
196
+ next
197
+ end
198
+ end
199
+ result
145
200
  end
146
201
  end
147
202
  end
@@ -211,20 +266,22 @@ class Chef
211
266
  response.error!
212
267
  end
213
268
 
214
- def install_snaps(snap_names)
215
- response = post_snaps(snap_names, "install", new_resource.channel, new_resource.options)
216
- id = get_id_from_async_response(response)
217
- wait_for_completion(id)
269
+ def install_snaps(snap_names, versions)
270
+ snap_names.each do |snap|
271
+ response = post_snap(snap, "install", new_resource.channel, new_resource.options)
272
+ id = get_id_from_async_response(response)
273
+ wait_for_completion(id)
274
+ end
218
275
  end
219
276
 
220
277
  def update_snaps(snap_names)
221
- response = post_snaps(snap_names, "refresh", new_resource.channel, new_resource.options)
278
+ response = post_snaps(snap_names, "refresh", nil, new_resource.options)
222
279
  id = get_id_from_async_response(response)
223
280
  wait_for_completion(id)
224
281
  end
225
282
 
226
283
  def uninstall_snaps(snap_names)
227
- response = post_snaps(snap_names, "remove", new_resource.channel, new_resource.options)
284
+ response = post_snaps(snap_names, "remove", nil, new_resource.options)
228
285
  id = get_id_from_async_response(response)
229
286
  wait_for_completion(id)
230
287
  end
@@ -278,18 +335,20 @@ class Chef
278
335
  "action" => action,
279
336
  "snaps" => snap_names,
280
337
  }
281
- if %w{install refresh switch}.include?(action)
338
+ if %w{install refresh switch}.include?(action) && channel
282
339
  request["channel"] = channel
283
340
  end
284
341
 
285
342
  # No defensive handling of params
286
343
  # Snap will throw the proper exception if called improperly
287
344
  # And we can provide that exception to the end user
288
- request["classic"] = true if options["classic"]
289
- request["devmode"] = true if options["devmode"]
290
- request["jailmode"] = true if options["jailmode"]
345
+ if options
346
+ request["classic"] = true if options.include?("classic")
347
+ request["devmode"] = true if options.include?("devmode")
348
+ request["jailmode"] = true if options.include?("jailmode")
349
+ request["ignore_validation"] = true if options.include?("ignore-validation")
350
+ end
291
351
  request["revision"] = revision unless revision.nil?
292
- request["ignore_validation"] = true if options["ignore-validation"]
293
352
  request
294
353
  end
295
354
 
@@ -305,12 +364,22 @@ class Chef
305
364
  call_snap_api("POST", "/v2/snaps", json)
306
365
  end
307
366
 
367
+ def post_snap(snap_name, action, channel, options, revision = nil)
368
+ json = generate_snap_json(snap_name, action, channel, options, revision = nil)
369
+ json.delete("snaps")
370
+ call_snap_api("POST", "/v2/snaps/#{snap_name}", json)
371
+ end
372
+
308
373
  def get_latest_package_version(name, channel)
309
374
  json = call_snap_api("GET", "/v2/find?name=#{name}")
310
375
  if json["status-code"] != 200
311
376
  raise Chef::Exceptions::Package, json["result"], caller
312
377
  end
313
378
 
379
+ unless json["result"][0]["channels"]["latest/#{channel}"]
380
+ raise Chef::Exceptions::Package, "No version of #{name} in channel #{channel}", caller
381
+ end
382
+
314
383
  # Return the version matching the channel
315
384
  json["result"][0]["channels"]["latest/#{channel}"]["version"]
316
385
  end