chef 12.15.19 → 12.16.42

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -1
  3. data/VERSION +1 -1
  4. data/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml +3 -1
  5. data/acceptance/Gemfile.lock +14 -14
  6. data/acceptance/data-collector/test/integration/default/serverspec/default_spec.rb +3 -11
  7. data/distro/common/html/knife_bootstrap.html +1 -1
  8. data/distro/common/man/man1/README.md +2 -2
  9. data/distro/common/man/man1/knife-client.1 +1 -1
  10. data/lib/chef/application.rb +7 -15
  11. data/lib/chef/application/client.rb +2 -2
  12. data/lib/chef/application/solo.rb +1 -1
  13. data/lib/chef/chef_class.rb +1 -0
  14. data/lib/chef/chef_fs/file_system/chef_server/cookbook_file.rb +3 -7
  15. data/lib/chef/chef_fs/file_system/chef_server/versioned_cookbook_dir.rb +1 -1
  16. data/lib/chef/data_collector.rb +83 -9
  17. data/lib/chef/data_collector/messages.rb +2 -1
  18. data/lib/chef/dsl/core.rb +1 -1
  19. data/lib/chef/dsl/declare_resource.rb +10 -4
  20. data/lib/chef/dsl/method_missing.rb +1 -1
  21. data/lib/chef/dsl/recipe.rb +1 -1
  22. data/lib/chef/dsl/universal.rb +1 -1
  23. data/lib/chef/event_dispatch/base.rb +3 -0
  24. data/lib/chef/http.rb +3 -4
  25. data/lib/chef/knife.rb +20 -2
  26. data/lib/chef/knife/core/generic_presenter.rb +18 -4
  27. data/lib/chef/knife/node_show.rb +0 -5
  28. data/lib/chef/knife/osc_user_show.rb +0 -1
  29. data/lib/chef/knife/ssl_fetch.rb +9 -5
  30. data/lib/chef/mixin/powershell_out.rb +1 -1
  31. data/lib/chef/mixin/shell_out.rb +1 -1
  32. data/lib/chef/node.rb +1 -5
  33. data/lib/chef/node/attribute.rb +70 -98
  34. data/lib/chef/node/attribute_collections.rb +28 -19
  35. data/lib/chef/node/common_api.rb +0 -6
  36. data/lib/chef/node/immutable_collections.rb +16 -79
  37. data/lib/chef/node/mixin/deep_merge_cache.rb +61 -0
  38. data/lib/chef/node/mixin/immutablize_array.rb +67 -0
  39. data/lib/chef/node/mixin/immutablize_hash.rb +54 -0
  40. data/lib/chef/node/mixin/state_tracking.rb +93 -0
  41. data/lib/chef/property.rb +4 -4
  42. data/lib/chef/provider/cron.rb +1 -1
  43. data/lib/chef/provider/group/suse.rb +23 -4
  44. data/lib/chef/provider/package.rb +43 -5
  45. data/lib/chef/provider/package/apt.rb +20 -0
  46. data/lib/chef/provider/package/windows/exe.rb +4 -3
  47. data/lib/chef/provider/package/windows/msi.rb +4 -3
  48. data/lib/chef/provider/package/yum.rb +20 -0
  49. data/lib/chef/provider/package/zypper.rb +20 -0
  50. data/lib/chef/provider/ruby_block.rb +1 -1
  51. data/lib/chef/provider/service/upstart.rb +25 -9
  52. data/lib/chef/provider/user.rb +4 -6
  53. data/lib/chef/provider/user/dscl.rb +8 -3
  54. data/lib/chef/provider/user/solaris.rb +5 -12
  55. data/lib/chef/resource.rb +19 -0
  56. data/lib/chef/resource/file.rb +1 -1
  57. data/lib/chef/resource/package.rb +1 -1
  58. data/lib/chef/resource/scm.rb +1 -7
  59. data/lib/chef/resource/yum_repository.rb +1 -1
  60. data/lib/chef/rest.rb +1 -0
  61. data/lib/chef/run_context.rb +12 -0
  62. data/lib/chef/version.rb +1 -1
  63. data/spec/data/trusted_certs/example_no_cn.crt +36 -0
  64. data/spec/functional/resource/group_spec.rb +1 -0
  65. data/spec/functional/resource/user/useradd_spec.rb +4 -2
  66. data/spec/integration/knife/data_bag_create_spec.rb +0 -3
  67. data/spec/integration/knife/environment_show_spec.rb +24 -4
  68. data/spec/integration/knife/node_environment_set_spec.rb +4 -1
  69. data/spec/integration/recipes/accumulator_spec.rb +232 -0
  70. data/spec/integration/recipes/resource_action_spec.rb +1 -1
  71. data/spec/spec_helper.rb +2 -2
  72. data/spec/support/shared/context/client.rb +12 -3
  73. data/spec/support/shared/integration/app_server_support.rb +1 -1
  74. data/spec/support/shared/integration/knife_support.rb +4 -1
  75. data/spec/unit/data_collector/messages_spec.rb +2 -0
  76. data/spec/unit/data_collector_spec.rb +158 -21
  77. data/spec/unit/http_spec.rb +1 -1
  78. data/spec/unit/knife/core/gem_glob_loader_spec.rb +1 -1
  79. data/spec/unit/knife/core/ui_spec.rb +10 -0
  80. data/spec/unit/knife/ssl_fetch_spec.rb +38 -0
  81. data/spec/unit/knife_spec.rb +31 -0
  82. data/spec/unit/mixin/powershell_out_spec.rb +25 -1
  83. data/spec/unit/node/attribute_spec.rb +46 -1
  84. data/spec/unit/node/vivid_mash_spec.rb +27 -89
  85. data/spec/unit/node_spec.rb +134 -3
  86. data/spec/unit/provider/deploy_spec.rb +1 -1
  87. data/spec/unit/provider/group/suse_spec.rb +90 -0
  88. data/spec/unit/provider/package/apt_spec.rb +22 -0
  89. data/spec/unit/provider/package/windows/msi_spec.rb +13 -4
  90. data/spec/unit/provider/package/windows_spec.rb +3 -3
  91. data/spec/unit/provider/package/yum_spec.rb +18 -0
  92. data/spec/unit/provider/package/zypper_spec.rb +64 -0
  93. data/spec/unit/provider/package_spec.rb +58 -0
  94. data/spec/unit/provider/remote_file/content_spec.rb +1 -1
  95. data/spec/unit/provider/service/upstart_service_spec.rb +13 -6
  96. data/spec/unit/provider/user/solaris_spec.rb +36 -9
  97. data/spec/unit/provider/user_spec.rb +6 -0
  98. data/spec/unit/resource/apt_repository_spec.rb +1 -1
  99. metadata +12 -5
@@ -0,0 +1,54 @@
1
+ #--
2
+ # Copyright:: Copyright 2016, Chef Software, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ class Chef
19
+ class Node
20
+ module Mixin
21
+ module ImmutablizeHash
22
+ DISALLOWED_MUTATOR_METHODS = [
23
+ :[]=,
24
+ :clear,
25
+ :collect!,
26
+ :default=,
27
+ :default_proc=,
28
+ :delete,
29
+ :delete_if,
30
+ :keep_if,
31
+ :map!,
32
+ :merge!,
33
+ :update,
34
+ :reject!,
35
+ :replace,
36
+ :select!,
37
+ :shift,
38
+ :write,
39
+ :write!,
40
+ :unlink,
41
+ :unlink!,
42
+ ]
43
+
44
+ # Redefine all of the methods that mutate a Hash to raise an error when called.
45
+ # This is the magic that makes this object "Immutable"
46
+ DISALLOWED_MUTATOR_METHODS.each do |mutator_method_name|
47
+ define_method(mutator_method_name) do |*args, &block|
48
+ raise Exceptions::ImmutableAttributeModification
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,93 @@
1
+ #--
2
+ # Copyright:: Copyright 2016, Chef Software, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ class Chef
19
+ class Node
20
+ module Mixin
21
+ module StateTracking
22
+ attr_reader :__path__
23
+ attr_reader :__root__
24
+ attr_reader :__node__
25
+ attr_reader :__precedence__
26
+
27
+ def initialize(data = nil, root = self, node = nil, precedence = nil)
28
+ # __path__ and __root__ must be nil when we call super so it knows
29
+ # to avoid resetting the cache on construction
30
+ data.nil? ? super() : super(data)
31
+ @__path__ = []
32
+ @__root__ = root
33
+ @__node__ = node
34
+ @__precedence__ = precedence
35
+ end
36
+
37
+ def [](key)
38
+ ret = super
39
+ next_path = [ __path__, convert_key(key) ].flatten.compact
40
+ copy_state_to(ret, next_path)
41
+ end
42
+
43
+ def []=(key, value)
44
+ ret = super
45
+ next_path = [ __path__, convert_key(key) ].flatten.compact
46
+ send_attribute_changed_event(next_path, value)
47
+ copy_state_to(ret, next_path)
48
+ end
49
+
50
+ protected
51
+
52
+ def __path__=(path)
53
+ @__path__ = path
54
+ end
55
+
56
+ def __root__=(root)
57
+ @__root__ = root
58
+ end
59
+
60
+ def __precedence__=(precedence)
61
+ @__precedence__ = precedence
62
+ end
63
+
64
+ def __node__=(node)
65
+ @__node__ = node
66
+ end
67
+
68
+ private
69
+
70
+ def send_attribute_changed_event(next_path, value)
71
+ if __node__ && __node__.run_context && __node__.run_context.events
72
+ __node__.run_context.events.attribute_changed(__precedence__, next_path, value)
73
+ end
74
+ end
75
+
76
+ def send_reset_cache(path = nil, key = nil)
77
+ next_path = [ path, key ].flatten.compact
78
+ __root__.reset_cache(next_path.first) if !__root__.nil? && __root__.respond_to?(:reset_cache) && !next_path.nil?
79
+ end
80
+
81
+ def copy_state_to(ret, next_path)
82
+ if ret.is_a?(StateTracking)
83
+ ret.__path__ = next_path
84
+ ret.__root__ = __root__
85
+ ret.__node__ = __node__
86
+ ret.__precedence__ = __precedence__
87
+ end
88
+ ret
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -522,22 +522,22 @@ class Chef
522
522
  # stack trace if you use `define_method`.
523
523
  declared_in.class_eval <<-EOM, __FILE__, __LINE__ + 1
524
524
  def #{name}(value=NOT_PASSED)
525
- raise "Property #{name} of \#{self} cannot be passed a block! If you meant to create a resource named #{name} instead, you'll need to first rename the property." if block_given?
525
+ raise "Property `#{name}` of `\#{self}` was incorrectly passed a block. Possible property-resource collision. To call a resource named `#{name}` either rename the property or else use `declare_resource(:#{name}, ...)`" if block_given?
526
526
  self.class.properties[#{name.inspect}].call(self, value)
527
527
  end
528
528
  def #{name}=(value)
529
- raise "Property #{name} of \#{self} cannot be passed a block! If you meant to create a resource named #{name} instead, you'll need to first rename the property." if block_given?
529
+ raise "Property `#{name}` of `\#{self}` was incorrectly passed a block. Possible property-resource collision. To call a resource named `#{name}` either rename the property or else use `declare_resource(:#{name}, ...)`" if block_given?
530
530
  self.class.properties[#{name.inspect}].set(self, value)
531
531
  end
532
532
  EOM
533
533
  rescue SyntaxError
534
534
  # If the name is not a valid ruby name, we use define_method.
535
535
  declared_in.define_method(name) do |value = NOT_PASSED, &block|
536
- raise "Property #{name} of #{self} cannot be passed a block! If you meant to create a resource named #{name} instead, you'll need to first rename the property." if block
536
+ raise "Property `#{name}` of `#{self}` was incorrectly passed a block! Possible property-resource collision. To call a resource named `#{name}` either rename the property or else use `declare_resource(:#{name}, ...)`" if block
537
537
  self.class.properties[name].call(self, value)
538
538
  end
539
539
  declared_in.define_method("#{name}=") do |value, &block|
540
- raise "Property #{name} of #{self} cannot be passed a block! If you meant to create a resource named #{name} instead, you'll need to first rename the property." if block
540
+ raise "Property `#{name}` of `#{self}` was incorrectly passed a block! Possible property-resource collision. To call a resource named `#{name}` either rename the property or else use `declare_resource(:#{name}, ...)`" if block
541
541
  self.class.properties[name].set(self, value)
542
542
  end
543
543
  end
@@ -199,7 +199,7 @@ class Chef
199
199
 
200
200
  def set_environment_var(attr_name, attr_value)
201
201
  if %w{MAILTO PATH SHELL HOME}.include?(attr_name)
202
- @current_resource.send(attr_name.downcase.to_sym, attr_value)
202
+ @current_resource.send(attr_name.downcase.to_sym, attr_value.gsub(/^"|"$/, ""))
203
203
  else
204
204
  @current_resource.environment(@current_resource.environment.merge(attr_name => attr_value))
205
205
  end
@@ -17,6 +17,7 @@
17
17
  #
18
18
 
19
19
  require "chef/provider/group/groupadd"
20
+ require "etc"
20
21
 
21
22
  class Chef
22
23
  class Provider
@@ -36,24 +37,42 @@ class Chef
36
37
  a.failure_message Chef::Exceptions::Group, "Could not find binary /usr/sbin/groupmod for #{@new_resource.name}"
37
38
  # No whyrun alternative: this component should be available in the base install of any given system that uses it
38
39
  end
40
+
41
+ requirements.assert(:create, :manage, :modify) do |a|
42
+ a.assertion do
43
+ begin
44
+ to_add(@new_resource.members).all? { |member| Etc.getpwnam(member) }
45
+ rescue
46
+ false
47
+ end
48
+ end
49
+ a.failure_message Chef::Exceptions::Group, "Could not add users #{to_add(@new_resource.members).join(", ")} to #{@new_resource.group_name}: one of these users does not exist"
50
+ a.whyrun "Could not find one of these users: #{to_add(@new_resource.members).join(", ")}. Assuming it will be created by a prior step"
51
+ end
39
52
  end
40
53
 
41
54
  def set_members(members)
42
- to_delete = @current_resource.members - members
43
- to_delete.each do |member|
55
+ to_remove(members).each do |member|
44
56
  remove_member(member)
45
57
  end
46
58
 
47
- to_add = members - @current_resource.members
48
- to_add.each do |member|
59
+ to_add(members).each do |member|
49
60
  add_member(member)
50
61
  end
51
62
  end
52
63
 
64
+ def to_add(members)
65
+ members - @current_resource.members
66
+ end
67
+
53
68
  def add_member(member)
54
69
  shell_out!("groupmod -A #{member} #{@new_resource.group_name}")
55
70
  end
56
71
 
72
+ def to_remove(members)
73
+ @current_resource.members - members
74
+ end
75
+
57
76
  def remove_member(member)
58
77
  shell_out!("groupmod -R #{member} #{@new_resource.group_name}")
59
78
  end
@@ -31,6 +31,8 @@ class Chef
31
31
  include Chef::Mixin::ShellOut
32
32
  extend Chef::Mixin::SubclassDirective
33
33
 
34
+ use_inline_resources
35
+
34
36
  # subclasses declare this if they want all their arguments as arrays of packages and names
35
37
  subclass_directive :use_multipackage_api
36
38
  # subclasses declare this if they want sources (filenames) pulled from their package names
@@ -81,7 +83,7 @@ class Chef
81
83
  end
82
84
  end
83
85
 
84
- def action_install
86
+ action :install do
85
87
  unless target_version_array.any?
86
88
  Chef::Log.debug("#{@new_resource} is already installed - nothing to do")
87
89
  return
@@ -116,7 +118,7 @@ class Chef
116
118
 
117
119
  private :install_description
118
120
 
119
- def action_upgrade
121
+ action :upgrade do
120
122
  if !target_version_array.any?
121
123
  Chef::Log.debug("#{@new_resource} no versions to upgrade - nothing to do")
122
124
  return
@@ -146,7 +148,7 @@ class Chef
146
148
 
147
149
  private :upgrade_description
148
150
 
149
- def action_remove
151
+ action :remove do
150
152
  if removing_package?
151
153
  description = @new_resource.version ? "version #{@new_resource.version} of " : ""
152
154
  converge_by("remove #{description}package #{@current_resource.package_name}") do
@@ -181,7 +183,7 @@ class Chef
181
183
  end
182
184
  end
183
185
 
184
- def action_purge
186
+ action :purge do
185
187
  if removing_package?
186
188
  description = @new_resource.version ? "version #{@new_resource.version} of" : ""
187
189
  converge_by("purge #{description} package #{@current_resource.package_name}") do
@@ -193,7 +195,7 @@ class Chef
193
195
  end
194
196
  end
195
197
 
196
- def action_reconfig
198
+ action :reconfig do
197
199
  if @current_resource.version == nil
198
200
  Chef::Log.debug("#{@new_resource} is NOT installed - nothing to do")
199
201
  return
@@ -218,6 +220,34 @@ class Chef
218
220
  end
219
221
  end
220
222
 
223
+ def action_lock
224
+ if package_locked(@new_resource.name, @new_resource.version) == false
225
+ description = @new_resource.version ? "version #{@new_resource.version} of " : ""
226
+ converge_by("lock #{description}package #{@current_resource.package_name}") do
227
+ multipackage_api_adapter(@current_resource.package_name, @new_resource.version) do |name, version|
228
+ lock_package(name, version)
229
+ Chef::Log.info("#{@new_resource} locked")
230
+ end
231
+ end
232
+ else
233
+ Chef::Log.debug("#{new_resource} is already locked")
234
+ end
235
+ end
236
+
237
+ def action_unlock
238
+ if package_locked(@new_resource.name, @new_resource.version) == true
239
+ description = @new_resource.version ? "version #{@new_resource.version} of " : ""
240
+ converge_by("unlock #{description}package #{@current_resource.package_name}") do
241
+ multipackage_api_adapter(@current_resource.package_name, @new_resource.version) do |name, version|
242
+ unlock_package(name, version)
243
+ Chef::Log.info("#{@new_resource} unlocked")
244
+ end
245
+ end
246
+ else
247
+ Chef::Log.debug("#{new_resource} is already unlocked")
248
+ end
249
+ end
250
+
221
251
  # @todo use composition rather than inheritance
222
252
 
223
253
  def multipackage_api_adapter(name, version)
@@ -252,6 +282,14 @@ class Chef
252
282
  raise( Chef::Exceptions::UnsupportedAction, "#{self} does not support :reconfig" )
253
283
  end
254
284
 
285
+ def lock_package(name, version)
286
+ raise( Chef::Exceptions::UnsupportedAction, "#{self} does not support :lock" )
287
+ end
288
+
289
+ def unlock_package(name, version)
290
+ raise( Chef::Exceptions::UnsupportedAction, "#{self} does not support :unlock" )
291
+ end
292
+
255
293
  # used by subclasses. deprecated. use #a_to_s instead.
256
294
  def expand_options(options)
257
295
  options ? " #{options}" : ""
@@ -70,6 +70,18 @@ class Chef
70
70
  @candidate_version ||= get_candidate_versions
71
71
  end
72
72
 
73
+ def package_locked(name, version)
74
+ islocked = false
75
+ locked = shell_out_with_timeout!("apt-mark showhold")
76
+ locked.stdout.each_line do |line|
77
+ line_package = line.strip
78
+ if line_package == name
79
+ islocked = true
80
+ end
81
+ end
82
+ return islocked
83
+ end
84
+
73
85
  def install_package(name, version)
74
86
  package_name = name.zip(version).map do |n, v|
75
87
  package_data[n][:virtual] ? n : "#{n}=#{v}"
@@ -105,6 +117,14 @@ class Chef
105
117
  run_noninteractive("dpkg-reconfigure", name)
106
118
  end
107
119
 
120
+ def lock_package(name, version)
121
+ run_noninteractive("apt-mark", new_resource.options, "hold", name)
122
+ end
123
+
124
+ def unlock_package(name, version)
125
+ run_noninteractive("apt-mark", new_resource.options, "unhold", name)
126
+ end
127
+
108
128
  private
109
129
 
110
130
  # Runs command via shell_out with magic environment to disable
@@ -89,9 +89,10 @@ class Chef
89
89
  end
90
90
 
91
91
  def current_installed_version
92
- @current_installed_version ||= uninstall_entries.count == 0 ? nil : begin
93
- uninstall_entries.map { |entry| entry.display_version }.uniq
94
- end
92
+ @current_installed_version ||=
93
+ if uninstall_entries.count != 0
94
+ uninstall_entries.map { |entry| entry.display_version }.uniq
95
+ end
95
96
  end
96
97
 
97
98
  # http://unattended.sourceforge.net/installers.php
@@ -50,7 +50,7 @@ class Chef
50
50
  Chef::Log.debug("#{new_resource} checking package status and version for #{product_code}")
51
51
  get_installed_version(product_code)
52
52
  else
53
- uninstall_entries.count == 0 ? nil : begin
53
+ if uninstall_entries.count != 0
54
54
  uninstall_entries.map { |entry| entry.display_version }.uniq
55
55
  end
56
56
  end
@@ -79,9 +79,10 @@ class Chef
79
79
  uninstall_version = new_resource.version || installed_version
80
80
  uninstall_entries.select { |entry| [uninstall_version].flatten.include?(entry.display_version) }
81
81
  .map { |version| version.uninstall_string }.uniq.each do |uninstall_string|
82
- Chef::Log.debug("#{new_resource} removing MSI package version using '#{uninstall_string}'")
82
+ uninstall_string = "msiexec /x #{uninstall_string.match(/{.*}/)}"
83
83
  uninstall_string += expand_options(new_resource.options)
84
- uninstall_string += " /Q" unless uninstall_string =~ / \/Q\b/
84
+ uninstall_string += " /q" unless uninstall_string.downcase =~ / \/q/
85
+ Chef::Log.debug("#{new_resource} removing MSI package version using '#{uninstall_string}'")
85
86
  shell_out!(uninstall_string, { :timeout => new_resource.timeout, :returns => new_resource.returns })
86
87
  end
87
88
  end
@@ -123,6 +123,18 @@ class Chef
123
123
  end
124
124
  end
125
125
 
126
+ def package_locked(name, version)
127
+ islocked = false
128
+ locked = shell_out_with_timeout!("yum versionlock")
129
+ locked.stdout.each_line do |line|
130
+ line_package = line.sub(/-[^-]*-[^-]*$/, "").split(":").last.strip
131
+ if line_package == name
132
+ islocked = true
133
+ end
134
+ end
135
+ return islocked
136
+ end
137
+
126
138
  # Standard Provider methods for Parent
127
139
  #
128
140
 
@@ -369,6 +381,14 @@ class Chef
369
381
  remove_package(name, version)
370
382
  end
371
383
 
384
+ def lock_package(name, version)
385
+ yum_command("-d0 -e0 -y#{expand_options(@new_resource.options)} versionlock add #{name}")
386
+ end
387
+
388
+ def unlock_package(name, version)
389
+ yum_command("-d0 -e0 -y#{expand_options(@new_resource.options)} versionlock delete #{name}")
390
+ end
391
+
372
392
  private
373
393
 
374
394
  def parse_arch(package_name)
@@ -75,6 +75,18 @@ class Chef
75
75
  end
76
76
  end
77
77
 
78
+ def package_locked(name, version)
79
+ islocked = false
80
+ locked = shell_out_with_timeout!("zypper locks")
81
+ locked.stdout.each_line do |line|
82
+ line_package = line.split("|").shift(2).last.strip
83
+ if line_package == name
84
+ islocked = true
85
+ end
86
+ end
87
+ return islocked
88
+ end
89
+
78
90
  def load_current_resource
79
91
  @current_resource = Chef::Resource::ZypperPackage.new(new_resource.name)
80
92
  current_resource.package_name(new_resource.package_name)
@@ -107,6 +119,14 @@ class Chef
107
119
  zypper_package("remove --clean-deps", name, version)
108
120
  end
109
121
 
122
+ def lock_package(name, version)
123
+ zypper_package("addlock", name, version)
124
+ end
125
+
126
+ def unlock_package(name, version)
127
+ zypper_package("removelock", name, version)
128
+ end
129
+
110
130
  private
111
131
 
112
132
  def zip(names, versions)