chef 13.5.3 → 13.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/bin/chef-shell +2 -0
  4. data/distro/powershell/chef/chef.psm1 +23 -2
  5. data/lib/chef/application/client.rb +1 -1
  6. data/lib/chef/chef_fs/config.rb +1 -1
  7. data/lib/chef/chef_fs/data_handler/data_bag_item_data_handler.rb +5 -0
  8. data/lib/chef/client.rb +3 -0
  9. data/lib/chef/cookbook/synchronizer.rb +2 -4
  10. data/lib/chef/data_bag.rb +4 -0
  11. data/lib/chef/deprecated.rb +10 -0
  12. data/lib/chef/knife/client_delete.rb +1 -1
  13. data/lib/chef/knife/node_delete.rb +1 -1
  14. data/lib/chef/knife/node_run_list_add.rb +1 -1
  15. data/lib/chef/knife/node_run_list_remove.rb +1 -1
  16. data/lib/chef/knife/role_env_run_list_add.rb +1 -1
  17. data/lib/chef/knife/role_run_list_add.rb +1 -1
  18. data/lib/chef/mixin/user_context.rb +1 -1
  19. data/lib/chef/platform/rebooter.rb +7 -3
  20. data/lib/chef/provider/dsc_script.rb +5 -1
  21. data/lib/chef/provider/package.rb +35 -4
  22. data/lib/chef/provider/package/apt.rb +7 -0
  23. data/lib/chef/provider/package/chocolatey.rb +24 -8
  24. data/lib/chef/provider/package/dnf.rb +14 -5
  25. data/lib/chef/provider/package/dnf/dnf_helper.py +10 -0
  26. data/lib/chef/provider/package/dnf/python_helper.rb +15 -0
  27. data/lib/chef/provider/package/rpm.rb +5 -0
  28. data/lib/chef/provider/package/windows.rb +15 -0
  29. data/lib/chef/provider/package/yum.rb +4 -20
  30. data/lib/chef/provider/package/zypper.rb +5 -1
  31. data/lib/chef/provider/route.rb +1 -1
  32. data/lib/chef/provider/windows_task.rb +19 -4
  33. data/lib/chef/provider/zypper_repository.rb +100 -12
  34. data/lib/chef/resource/deploy.rb +6 -0
  35. data/lib/chef/resource/dnf_package.rb +9 -2
  36. data/lib/chef/resource/windows_task.rb +12 -7
  37. data/lib/chef/resource/zypper_package.rb +1 -0
  38. data/lib/chef/resource/zypper_repository.rb +1 -0
  39. data/lib/chef/util/dsc/lcm_output_parser.rb +57 -2
  40. data/lib/chef/util/dsc/local_configuration_manager.rb +16 -16
  41. data/lib/chef/version.rb +1 -1
  42. data/spec/functional/rebooter_spec.rb +22 -11
  43. data/spec/functional/resource/windows_task_spec.rb +25 -0
  44. data/spec/unit/chef_fs/data_handler/data_bag_item_data_handler.rb +79 -0
  45. data/spec/unit/client_spec.rb +11 -0
  46. data/spec/unit/cookbook/synchronizer_spec.rb +30 -0
  47. data/spec/unit/knife/data_bag_create_spec.rb +8 -0
  48. data/spec/unit/mixin/user_context_spec.rb +1 -2
  49. data/spec/unit/provider/package/chocolatey_spec.rb +3 -1
  50. data/spec/unit/provider/package/rubygems_spec.rb +5 -0
  51. data/spec/unit/provider/package/zypper_spec.rb +8 -0
  52. data/spec/unit/provider/remote_file/network_file_spec.rb +1 -1
  53. data/spec/unit/provider/route_spec.rb +2 -0
  54. data/spec/unit/provider/windows_task_spec.rb +28 -0
  55. data/spec/unit/provider/zypper_repository_spec.rb +124 -0
  56. data/spec/unit/provider_resolver_spec.rb +4 -1
  57. data/spec/unit/resource/windows_task_spec.rb +8 -2
  58. data/spec/unit/resource/zypper_repository_spec.rb +11 -9
  59. data/spec/unit/util/dsc/lcm_output_parser_spec.rb +102 -18
  60. data/spec/unit/util/dsc/local_configuration_manager_spec.rb +2 -1
  61. metadata +6 -4
@@ -61,6 +61,15 @@ class Chef
61
61
  start if stdin.nil?
62
62
  end
63
63
 
64
+ def compare_versions(version1, version2)
65
+ with_helper do
66
+ json = build_version_query("versioncompare", [version1, version2])
67
+ Chef::Log.debug "sending '#{json}' to python helper"
68
+ stdin.syswrite json + "\n"
69
+ stdout.sysread(4096).chomp.to_i
70
+ end
71
+ end
72
+
64
73
  # @returns Array<Version>
65
74
  def query(action, provides, version = nil, arch = nil)
66
75
  with_helper do
@@ -109,6 +118,12 @@ class Chef
109
118
  FFI_Yajl::Encoder.encode(hash)
110
119
  end
111
120
 
121
+ def build_version_query(action, versions)
122
+ hash = { "action" => action }
123
+ hash["versions"] = versions
124
+ FFI_Yajl::Encoder.encode(hash)
125
+ end
126
+
112
127
  def parse_response(output)
113
128
  array = output.split.map { |x| x == "nil" ? nil : x }
114
129
  array.each_slice(3).map { |x| Version.new(*x) }.first
@@ -18,6 +18,7 @@
18
18
  require "chef/provider/package"
19
19
  require "chef/resource/package"
20
20
  require "chef/mixin/get_source_from_package"
21
+ require "chef/provider/package/yum/rpm_utils"
21
22
 
22
23
  class Chef
23
24
  class Provider
@@ -109,6 +110,10 @@ class Chef
109
110
 
110
111
  private
111
112
 
113
+ def version_compare(v1, v2)
114
+ Chef::Provider::Package::Yum::RPMVersion.parse(v1) <=> Chef::Provider::Package::Yum::RPMVersion.parse(v2)
115
+ end
116
+
112
117
  def uri_scheme?(str)
113
118
  scheme = URI.split(str).first
114
119
  return false unless scheme
@@ -165,6 +165,10 @@ class Chef
165
165
  #
166
166
  # @return [Boolean] true if new_version is equal to or included in current_version
167
167
  def target_version_already_installed?(current_version, new_version)
168
+ version_equals?(current_version, new_version)
169
+ end
170
+
171
+ def version_equals?(current_version, new_version)
168
172
  Chef::Log.debug("Checking if #{new_resource} version '#{new_version}' is already installed. #{current_version} is currently installed")
169
173
  if current_version.is_a?(Array)
170
174
  current_version.include?(new_version)
@@ -179,6 +183,17 @@ class Chef
179
183
 
180
184
  private
181
185
 
186
+ def version_compare(v1, v2)
187
+ if v1 == "latest" || v2 == "latest"
188
+ return 0
189
+ end
190
+
191
+ gem_v1 = Gem::Version.new(v1)
192
+ gem_v2 = Gem::Version.new(v2)
193
+
194
+ gem_v1 <=> gem_v2
195
+ end
196
+
182
197
  def uninstall_registry_entries
183
198
  @uninstall_registry_entries ||= Chef::Provider::Package::Windows::RegistryUninstallEntry.find_entries(new_resource.package_name)
184
199
  end
@@ -156,26 +156,6 @@ class Chef
156
156
  yum_command("-d0 -e0 -y#{expand_options(new_resource.options)} versionlock delete #{unlock_str}")
157
157
  end
158
158
 
159
- # Keep upgrades from trying to install an older candidate version. Can happen when a new
160
- # version is installed then removed from a repository, now the older available version
161
- # shows up as a viable install candidate.
162
- #
163
- # Can be done in upgrade_package but an upgraded from->to log message slips out
164
- #
165
- # Hacky - better overall solution? Custom compare in Package provider?
166
- def action_upgrade
167
- # Could be uninstalled or have no candidate
168
- if current_resource.version.nil? || !candidate_version_array.any?
169
- super
170
- elsif candidate_version_array.zip(current_version_array).any? do |c, i|
171
- RPMVersion.parse(c) > RPMVersion.parse(i)
172
- end
173
- super
174
- else
175
- Chef::Log.debug("#{new_resource} is at the latest version - nothing to do")
176
- end
177
- end
178
-
179
159
  private
180
160
 
181
161
  #
@@ -190,6 +170,10 @@ class Chef
190
170
  end
191
171
  end
192
172
 
173
+ def version_compare(v1, v2)
174
+ RPMVersion.parse(v1) <=> RPMVersion.parse(v2)
175
+ end
176
+
193
177
  # Enable or disable YumCache extra_repo_control
194
178
  def manage_extra_repo_control
195
179
  if new_resource.options
@@ -103,7 +103,7 @@ class Chef
103
103
  end
104
104
 
105
105
  def install_package(name, version)
106
- zypper_package("install", *options, "--auto-agree-with-licenses", name, version)
106
+ zypper_package("install", *options, "--auto-agree-with-licenses", allow_downgrade, name, version)
107
107
  end
108
108
 
109
109
  def upgrade_package(name, version)
@@ -147,6 +147,10 @@ class Chef
147
147
  def gpg_checks
148
148
  "--no-gpg-checks" unless new_resource.gpg_check
149
149
  end
150
+
151
+ def allow_downgrade
152
+ "--oldpackage" if new_resource.allow_downgrade
153
+ end
150
154
  end
151
155
  end
152
156
  end
@@ -186,7 +186,7 @@ class Chef
186
186
  network_file_name = "/etc/sysconfig/network"
187
187
  converge_by("write route default route to #{network_file_name}") do
188
188
  Chef::Log.debug("#{new_resource} writing default route #{new_resource.gateway} to #{network_file_name}")
189
- if ::File.exists?(network_file_name)
189
+ if ::File.exist?(network_file_name)
190
190
  network_file = ::Chef::Util::FileEdit.new(network_file_name)
191
191
  network_file.search_file_replace_line /^GATEWAY=/, "GATEWAY=#{new_resource.gateway}"
192
192
  network_file.insert_line_if_no_match /^GATEWAY=/, "GATEWAY=#{new_resource.gateway}"
@@ -89,11 +89,17 @@ class Chef
89
89
  basic_validation
90
90
  options = {}
91
91
  options["F"] = "" if new_resource.force || task_need_update?
92
- options["SC"] = schedule
92
+ if schedule == :none
93
+ options["SC"] = :once
94
+ options["ST"] = "00:00"
95
+ options["SD"] = convert_user_date_to_system_date "12/12/2012"
96
+ else
97
+ options["SC"] = schedule
98
+ options["ST"] = new_resource.start_time unless new_resource.start_time.nil?
99
+ options["SD"] = convert_user_date_to_system_date new_resource.start_day unless new_resource.start_day.nil?
100
+ end
93
101
  options["MO"] = new_resource.frequency_modifier if frequency_modifier_allowed
94
102
  options["I"] = new_resource.idle_time unless new_resource.idle_time.nil?
95
- options["SD"] = convert_user_date_to_system_date new_resource.start_day unless new_resource.start_day.nil?
96
- options["ST"] = new_resource.start_time unless new_resource.start_time.nil?
97
103
  options["TR"] = new_resource.command
98
104
  options["RU"] = new_resource.user
99
105
  options["RP"] = new_resource.password if use_password?
@@ -277,6 +283,7 @@ class Chef
277
283
  :daily => "Triggers/CalendarTrigger/RandomDelay",
278
284
  :weekly => "Triggers/CalendarTrigger/RandomDelay",
279
285
  :monthly => "Triggers/CalendarTrigger/RandomDelay",
286
+ :none => "Triggers",
280
287
  }
281
288
 
282
289
  xml_element_mapping = {
@@ -297,6 +304,12 @@ class Chef
297
304
 
298
305
  doc = REXML::Document.new(xml_cmd.stdout)
299
306
 
307
+ if new_resource.frequency == :none
308
+ doc.root.elements.delete(xml_element_mapping["random_delay"])
309
+ trigger_element = REXML::Element.new(xml_element_mapping["random_delay"])
310
+ doc.root.elements.add(trigger_element)
311
+ end
312
+
300
313
  options.each do |option|
301
314
  Chef::Log.debug 'Removing former #{option} if any'
302
315
  doc.root.elements.delete(xml_element_mapping[option])
@@ -410,7 +423,8 @@ class Chef
410
423
 
411
424
  task[:idle_time] = root.elements["Settings/IdleSettings/Duration"].text if root.elements["Settings/IdleSettings/Duration"] && task[:on_idle]
412
425
 
413
- task[:once] = true if !(task[:repetition_interval] || task[:schedule_by_day] || task[:schedule_by_week] || task[:schedule_by_month] || task[:on_logon] || task[:onstart] || task[:on_idle])
426
+ task[:none] = true if root.elements["Triggers/"] && root.elements["Triggers/"].entries.empty?
427
+ task[:once] = true if !(task[:repetition_interval] || task[:schedule_by_day] || task[:schedule_by_week] || task[:schedule_by_month] || task[:on_logon] || task[:onstart] || task[:on_idle] || task[:none])
414
428
  task[:execution_time_limit] = root.elements["Settings/ExecutionTimeLimit"].text if root.elements["Settings/ExecutionTimeLimit"] #by default PT72H
415
429
  task[:random_delay] = root.elements["Triggers/TimeTrigger/RandomDelay"].text if root.elements["Triggers/TimeTrigger/RandomDelay"]
416
430
  task[:random_delay] = root.elements["Triggers/CalendarTrigger/RandomDelay"].text if root.elements["Triggers/CalendarTrigger/RandomDelay"]
@@ -481,6 +495,7 @@ class Chef
481
495
  current_resource.frequency(:onstart) if task_hash[:onstart]
482
496
  current_resource.frequency(:on_idle) if task_hash[:on_idle]
483
497
  current_resource.frequency(:once) if task_hash[:once]
498
+ current_resource.frequency(:none) if task_hash[:none]
484
499
  end
485
500
 
486
501
  def set_current_idle_time(idle_time)
@@ -18,24 +18,25 @@
18
18
 
19
19
  require "chef/resource"
20
20
  require "chef/dsl/declare_resource"
21
- require "chef/mixin/which"
22
21
  require "chef/provider/noop"
22
+ require "chef/mixin/shell_out"
23
23
  require "shellwords"
24
24
 
25
25
  class Chef
26
26
  class Provider
27
27
  class ZypperRepository < Chef::Provider
28
-
29
- extend Chef::Mixin::Which
30
-
31
- provides :zypper_repository do
32
- which "zypper"
33
- end
28
+ provides :zypper_repository, platform_family: "suse"
34
29
 
35
30
  def load_current_resource
36
31
  end
37
32
 
38
33
  action :create do
34
+ if new_resource.gpgautoimportkeys
35
+ install_gpg_key(new_resource.gpgkey)
36
+ else
37
+ Chef::Log.debug("'gpgautoimportkeys' property is set to false. Skipping key import.")
38
+ end
39
+
39
40
  declare_resource(:template, "/etc/zypp/repos.d/#{escaped_repo_name}.repo") do
40
41
  if template_available?(new_resource.source)
41
42
  source new_resource.source
@@ -51,14 +52,14 @@ class Chef
51
52
  end
52
53
 
53
54
  action :delete do
54
- declare_resource(:execute, "zypper removerepo #{escaped_repo_name}") do
55
- only_if "zypper lr #{escaped_repo_name}"
55
+ declare_resource(:execute, "zypper --quiet --non-interactive removerepo #{escaped_repo_name}") do
56
+ only_if "zypper --quiet lr #{escaped_repo_name}"
56
57
  end
57
58
  end
58
59
 
59
60
  action :refresh do
60
- declare_resource(:execute, "zypper#{' --gpg-auto-import-keys' if new_resource.gpgautoimportkeys} --quiet --no-confirm refresh --force #{escaped_repo_name}") do
61
- only_if "zypper lr #{escaped_repo_name}"
61
+ declare_resource(:execute, "zypper --quiet --non-interactive refresh --force #{escaped_repo_name}") do
62
+ only_if "zypper --quiet lr #{escaped_repo_name}"
62
63
  end
63
64
  end
64
65
 
@@ -66,14 +67,101 @@ class Chef
66
67
  alias_method :action_remove, :action_delete
67
68
 
68
69
  # zypper repos are allowed to have spaces in the names
70
+ # @return [String] escaped repo string
69
71
  def escaped_repo_name
70
72
  Shellwords.escape(new_resource.repo_name)
71
73
  end
72
74
 
75
+ # return the specified cookbook name or the cookbook containing the
76
+ # resource.
77
+ #
78
+ # @return [String] name of the cookbook
79
+ def cookbook_name
80
+ new_resource.cookbook || new_resource.cookbook_name
81
+ end
82
+
83
+ # determine if a template file is available in the current run
84
+ # @param [String] path the path to the template file
85
+ #
86
+ # @return [Boolean] template file exists or doesn't
73
87
  def template_available?(path)
74
- !path.nil? && run_context.has_template_in_cookbook?(new_resource.cookbook_name, path)
88
+ !path.nil? && run_context.has_template_in_cookbook?(cookbook_name, path)
89
+ end
90
+
91
+ # determine if a cookbook file is available in the run
92
+ # @param [String] path the path to the template file
93
+ #
94
+ # @return [Boolean] cookbook file exists or doesn't
95
+ def has_cookbook_file?(fn)
96
+ run_context.has_cookbook_file_in_cookbook?(cookbook_name, fn)
97
+ end
98
+
99
+ # Given the provided key URI determine what kind of chef resource we need
100
+ # to fetch the key
101
+ # @param [String] uri the uri of the gpg key (local path or http URL)
102
+ #
103
+ # @raise [Chef::Exceptions::FileNotFound] Key isn't remote or found in the current run
104
+ #
105
+ # @return [Symbol] :remote_file or :cookbook_file
106
+ def key_type(uri)
107
+ if uri.start_with?("http")
108
+ Chef::Log.debug("Will use :remote_file resource to cache the gpg key locally")
109
+ :remote_file
110
+ elsif has_cookbook_file?(uri)
111
+ Chef::Log.debug("Will use :cookbook_file resource to cache the gpg key locally")
112
+ :cookbook_file
113
+ else
114
+ raise Chef::Exceptions::FileNotFound, "Cannot determine location of gpgkey. Must start with 'http' or be a file managed by Chef."
115
+ end
75
116
  end
76
117
 
118
+ # is the provided key already installed
119
+ # @param [String] key_path the path to the key on the local filesystem
120
+ #
121
+ # @return [boolean] is the key already known by rpm
122
+ def key_installed?(key_path)
123
+ so = shell_out("rpm -qa gpg-pubkey*")
124
+ # expected output & match: http://rubular.com/r/RdF7EcXEtb
125
+ status = /gpg-pubkey-#{key_fingerprint(key_path)}/.match(so.stdout)
126
+ Chef::Log.debug("GPG key at #{key_path} is known by rpm? #{status ? "true" : "false"}")
127
+ status
128
+ end
129
+
130
+ # extract the gpg key fingerprint from a local file
131
+ # @param [String] key_path the path to the key on the local filesystem
132
+ #
133
+ # @return [String] the fingerprint of the key
134
+ def key_fingerprint(key_path)
135
+ so = shell_out!("gpg --with-fingerprint #{key_path}")
136
+ # expected output and match: http://rubular.com/r/BpfMjxySQM
137
+ fingerprint = /pub\s*\S*\/(\S*)/.match(so.stdout)[1].downcase
138
+ Chef::Log.debug("GPG fingerprint of key at #{key_path} is #{fingerprint}")
139
+ fingerprint
140
+ end
141
+
142
+ # install the provided gpg key
143
+ # @param [String] uri the uri of the local or remote gpg key
144
+ def install_gpg_key(uri)
145
+ unless uri
146
+ Chef::Log.debug("'gpgkey' property not provided or set to nil. Skipping key import.")
147
+ return
148
+ end
149
+
150
+ cached_keyfile = ::File.join(Chef::Config[:file_cache_path], uri.split("/")[-1])
151
+
152
+ declare_resource(key_type(new_resource.gpgkey), cached_keyfile) do
153
+ source uri
154
+ mode "0644"
155
+ sensitive new_resource.sensitive
156
+ action :create
157
+ end
158
+
159
+ declare_resource(:execute, "import gpg key from #{new_resource.gpgkey}") do
160
+ command "/bin/rpm --import #{cached_keyfile}"
161
+ not_if { key_installed?(cached_keyfile) }
162
+ action :run
163
+ end
164
+ end
77
165
  end
78
166
  end
79
167
  end
@@ -84,6 +84,12 @@ class Chef
84
84
  @checkout_branch = "deploy"
85
85
  end
86
86
 
87
+ # This resource is deprecated.
88
+ def after_created
89
+ Chef.deprecated(:deploy_resource, "The #{resource_name} resource (#{source_line}) is deprecated and will be removed from Chef core in 14.0 (April 2018). " \
90
+ "A migration cookbook will be available in the future, see the deprecation documentation for more information.")
91
+ end
92
+
87
93
  # where the checked out/cloned code goes
88
94
  def destination
89
95
  @destination ||= shared_path + "/#{@repository_cache}"
@@ -29,8 +29,15 @@ class Chef
29
29
 
30
30
  allowed_actions :install, :upgrade, :remove, :purge, :reconfig, :lock, :unlock, :flush_cache
31
31
 
32
- provides :package, platform_family: %w{rhel fedora amazon} do
33
- which("dnf") && shell_out("rpm -q dnf").stdout =~ /^dnf-[1-9]/
32
+ # all rhel variants >= 8 will use DNF
33
+ provides :package, platform_family: "rhel", platform_version: ">= 8"
34
+
35
+ # fedora >= 22 uses DNF
36
+ provides :package, platform: "fedora", platform_version: ">= 22"
37
+
38
+ # amazon will eventually use DNF
39
+ provides :package, platform: "amazon" do
40
+ which("dnf")
34
41
  end
35
42
 
36
43
  provides :dnf_package
@@ -51,7 +51,8 @@ class Chef
51
51
  :once,
52
52
  :on_logon,
53
53
  :onstart,
54
- :on_idle], default: :hourly
54
+ :on_idle,
55
+ :none], default: :hourly
55
56
  property :start_day, String
56
57
  property :start_time, String
57
58
  property :day, [String, Integer]
@@ -81,7 +82,7 @@ class Chef
81
82
  execution_time_limit("PT72H")
82
83
  end
83
84
 
84
- validate_start_time(start_time) if frequency == :once
85
+ validate_start_time(start_time, frequency)
85
86
  validate_start_day(start_day, frequency) if start_day
86
87
  validate_user_and_password(user, password)
87
88
  validate_interactive_setting(interactive_enabled, password)
@@ -94,7 +95,7 @@ class Chef
94
95
  private
95
96
 
96
97
  def validate_random_delay(random_delay, frequency)
97
- if [:once, :on_logon, :onstart, :on_idle].include? frequency
98
+ if [:once, :on_logon, :onstart, :on_idle, :none].include? frequency
98
99
  raise ArgumentError, "`random_delay` property is supported only for frequency :minute, :hourly, :daily, :weekly and :monthly"
99
100
  end
100
101
 
@@ -102,13 +103,17 @@ class Chef
102
103
  end
103
104
 
104
105
  def validate_start_day(start_day, frequency)
105
- if [:once, :on_logon, :onstart, :on_idle].include? frequency
106
+ if [:once, :on_logon, :onstart, :on_idle, :none].include? frequency
106
107
  raise ArgumentError, "`start_day` property is not supported with frequency: #{frequency}"
107
108
  end
108
109
  end
109
110
 
110
- def validate_start_time(start_time)
111
- raise ArgumentError, "`start_time` needs to be provided with `frequency :once`" unless start_time
111
+ def validate_start_time(start_time, frequency)
112
+ if frequency == :once
113
+ raise ArgumentError, "`start_time` needs to be provided with `frequency :once`" unless start_time
114
+ elsif frequency == :none
115
+ raise ArgumentError, "`start_time` property is not supported with `frequency :none`" if start_time
116
+ end
112
117
  end
113
118
 
114
119
  SYSTEM_USERS = ['NT AUTHORITY\SYSTEM', "SYSTEM", 'NT AUTHORITY\LOCALSERVICE', 'NT AUTHORITY\NETWORKSERVICE', 'BUILTIN\USERS', "USERS"].freeze
@@ -132,7 +137,7 @@ class Chef
132
137
  end
133
138
 
134
139
  def validate_create_frequency_modifier(frequency, frequency_modifier)
135
- # Currently is handled in create action 'frequency_modifier_allowed' line. Does not allow for frequency_modifier for once,onstart,onlogon,onidle
140
+ # Currently is handled in create action 'frequency_modifier_allowed' line. Does not allow for frequency_modifier for once,onstart,onlogon,onidle,none
136
141
  # Note that 'OnEvent' is not a supported frequency.
137
142
  unless frequency.nil? || frequency_modifier.nil?
138
143
  case frequency
@@ -25,6 +25,7 @@ class Chef
25
25
  provides :package, platform_family: "suse"
26
26
 
27
27
  property :gpg_check, [ TrueClass, FalseClass ], default: lazy { Chef::Config[:zypper_check_gpg] }
28
+ property :allow_downgrade, [ true, false ], default: false
28
29
  end
29
30
  end
30
31
  end