chef-utils 16.11.7 → 17.2.29

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2aba5cf4ef0af21548b2a6b1d01d08ce830bf347216ef11f610ddcc847711f2c
4
- data.tar.gz: a11924b8feac484b4bcf879866ebd19553c2824737acbdd4e252a8e1d5db3985
3
+ metadata.gz: 860b9ebe97ac8f22fcdeebb27bf112b4535edcffbb189dc7543ab4c30a1542cb
4
+ data.tar.gz: 7a3e2aab7feb50f45f4a57679d5644a8a4beab2b96eeaaf86032c2bdf6392aca
5
5
  SHA512:
6
- metadata.gz: 227c1cbfe22df9c754d4b72ca919e980d5ac30448a6bbc1640fcc6a45e105b132da1783a6e15fe5b8b068e1cc4d418099289e3505c17095cc71530fa01bb3998
7
- data.tar.gz: 6c8b4dede11e400fa60e9ab4a4ff2cf23da091c9b939f830fd9051e54fe2f1e1e5f760c85b01805916fc97fc98c65fcae7c22efb39fd8a3ab9b1685a44bfb41e
6
+ metadata.gz: 5c749b979ec103b8f2b052cf2054747adc4e837f69251269412e37f2af6b90c3007dd41c94622de06d40d76964eb0f56d975d02d9965b22117a66e629ba8075d
7
+ data.tar.gz: be28ce9afeead797ba7783a8e60635061589c1e5b4343b76f8e927d1d82e25d04da7bd0910728270839259c45e7a0292b0a6e0cc481093aa4d7d14528f7f6465
data/chef-utils.gemspec CHANGED
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
13
13
  spec.homepage = "https://github.com/chef/chef/tree/master/chef-utils"
14
14
  spec.license = "Apache-2.0"
15
15
 
16
- spec.required_ruby_version = ">= 2.6.0"
16
+ spec.required_ruby_version = ">= 2.6"
17
17
 
18
18
  spec.metadata = {
19
19
  "bug_tracker_uri" => "https://github.com/chef/chef/issues",
@@ -41,6 +41,10 @@ Gem::Specification.new do |spec|
41
41
  # ABSOLUTELY NO EXCEPTIONS
42
42
  #
43
43
 
44
+ # concurrent-ruby is: 1. lightweight, 2. has zero deps, 3. is external to chef
45
+ # this is used for the parallel_map enumerable extension for lightweight threading
46
+ spec.add_dependency "concurrent-ruby"
47
+
44
48
  spec.files = %w{Rakefile LICENSE} + Dir.glob("*.gemspec") +
45
49
  Dir.glob("{lib,spec}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) }
46
50
  end
@@ -15,6 +15,25 @@ module ChefUtils
15
15
  PRODUCT = "Chef Automate"
16
16
  end
17
17
 
18
+ class Cli
19
+ # the chef-cli product name
20
+ PRODUCT = "Chef CLI"
21
+
22
+ # the chef-cli gem
23
+ GEM = "chef-cli"
24
+ end
25
+
26
+ class Habitat
27
+ # name of the Habitat product
28
+ PRODUCT = "Chef Habitat"
29
+
30
+ # A short designation for the product
31
+ SHORT = "habitat"
32
+
33
+ # The hab cli binary
34
+ EXEC = "hab"
35
+ end
36
+
18
37
  class Infra
19
38
  # When referencing a product directly, like Chef (Now Chef Infra)
20
39
  PRODUCT = "Chef Infra Client"
@@ -42,6 +61,17 @@ module ChefUtils
42
61
  # The suffix for Chef's /etc/chef, /var/chef and C:\\Chef directories
43
62
  # "chef" => /etc/cinc, /var/cinc, C:\\cinc
44
63
  DIR_SUFFIX = "chef"
64
+
65
+ # The client's gem
66
+ GEM = "chef"
67
+ end
68
+
69
+ class Inspec
70
+ # The InSpec product name
71
+ PRODUCT = "Chef InSpec"
72
+
73
+ # The inspec binary
74
+ EXEC = "inspec"
45
75
  end
46
76
 
47
77
  class Org
@@ -63,6 +93,9 @@ module ChefUtils
63
93
 
64
94
  # knife documentation page
65
95
  KNIFE_DOCS = "https://docs.chef.io/workstation/knife/"
96
+
97
+ # the name of the overall infra product
98
+ PRODUCT = "Chef Infra"
66
99
  end
67
100
 
68
101
  class Server
@@ -87,6 +120,17 @@ module ChefUtils
87
120
  EXEC = "chef-solo"
88
121
  end
89
122
 
123
+ class Workstation
124
+ # The full marketing name of the product
125
+ PRODUCT = "Chef Workstation"
126
+
127
+ # The suffix for Chef Workstation's /opt/chef-workstation or C:\\opscode\chef-workstation
128
+ DIR_SUFFIX = "chef-workstation"
129
+
130
+ # Workstation banner/help text
131
+ DOCS = "https://docs.chef.io/workstation/"
132
+ end
133
+
90
134
  class Zero
91
135
  # chef-zero executable
92
136
  PRODUCT = "Chef Infra Zero"
@@ -23,7 +23,7 @@ module ChefUtils
23
23
  module Cloud
24
24
  include Internal
25
25
 
26
- # Determine if the current node is "in the cloud".
26
+ # Determine if the current node is running in a known cloud.
27
27
  #
28
28
  # @param [Chef::Node] node the node to check
29
29
  # @since 15.8
@@ -35,7 +35,18 @@ module ChefUtils
35
35
  !node["cloud"].nil?
36
36
  end
37
37
 
38
- # Return true if the current current node is in EC2.
38
+ # Determine if the current node is running in Alibaba Cloud
39
+ #
40
+ # @param [Chef::Node] node the node to check
41
+ # @since 17.0
42
+ #
43
+ # @return [Boolean]
44
+ #
45
+ def alibaba?(node = __getnode)
46
+ node.key?("alibaba")
47
+ end
48
+
49
+ # Determine if the current node is running in AWS EC2.
39
50
  #
40
51
  # @param [Chef::Node] node the node to check
41
52
  # @since 15.8
@@ -46,7 +57,7 @@ module ChefUtils
46
57
  node.key?("ec2")
47
58
  end
48
59
 
49
- # Return true if the current current node is in GCE.
60
+ # Determine if the current node running in Google Compute Engine (GCE).
50
61
  #
51
62
  # @param [Chef::Node] node the node to check
52
63
  # @since 15.8
@@ -57,7 +68,7 @@ module ChefUtils
57
68
  node.key?("gce")
58
69
  end
59
70
 
60
- # Return true if the current current node is in Rackspace.
71
+ # Determine if the current node is running in Rackspace.
61
72
  #
62
73
  # @param [Chef::Node] node the node to check
63
74
  # @since 15.8
@@ -68,7 +79,7 @@ module ChefUtils
68
79
  node.key?("rackspace")
69
80
  end
70
81
 
71
- # Return true if the current current node is in Eucalyptus.
82
+ # Determine if the current node is running in Eucalyptus.
72
83
  #
73
84
  # @param [Chef::Node] node the node to check
74
85
  # @since 15.8
@@ -81,7 +92,7 @@ module ChefUtils
81
92
  # chef-sugar backcompat method
82
93
  alias_method :euca?, :eucalyptus?
83
94
 
84
- # Return true if the current current node is in Linode.
95
+ # Determine if the current node is running in Linode.
85
96
  #
86
97
  # @param [Chef::Node] node the node to check
87
98
  # @since 15.8
@@ -92,7 +103,7 @@ module ChefUtils
92
103
  node.key?("linode")
93
104
  end
94
105
 
95
- # Return true if the current current node is in OpenStack.
106
+ # Determine if the current node is running in OpenStack.
96
107
  #
97
108
  # @param [Chef::Node] node the node to check
98
109
  # @since 15.8
@@ -103,7 +114,7 @@ module ChefUtils
103
114
  node.key?("openstack")
104
115
  end
105
116
 
106
- # Return true if the current current node is in Azure.
117
+ # Determine if the current node is running in Microsoft Azure.
107
118
  #
108
119
  # @param [Chef::Node] node the node to check
109
120
  # @since 15.8
@@ -114,7 +125,7 @@ module ChefUtils
114
125
  node.key?("azure")
115
126
  end
116
127
 
117
- # Return true if the current current node is in DigitalOcean.
128
+ # Determine if the current node is running in DigitalOcean.
118
129
  #
119
130
  # @param [Chef::Node] node the node to check
120
131
  # @since 15.8
@@ -127,7 +138,7 @@ module ChefUtils
127
138
  # chef-sugar backcompat method
128
139
  alias_method :digitalocean?, :digital_ocean?
129
140
 
130
- # Return true if the current current node is in SoftLayer.
141
+ # Determine if the current node is running in SoftLayer (IBM Cloud).
131
142
  #
132
143
  # @param [Chef::Node] node the node to check
133
144
  # @since 15.8
@@ -29,6 +29,17 @@ module ChefUtils
29
29
  module Introspection
30
30
  include TrainHelpers
31
31
 
32
+ # Determine if the node is using the Chef Effortless pattern in which the Chef Infra Client is packaged using Chef Habitat
33
+ #
34
+ # @param [Chef::Node] node the node to check
35
+ # @since 17.0
36
+ #
37
+ # @return [Boolean]
38
+ #
39
+ def effortless?(node = __getnode)
40
+ !!(node && node.read("chef_packages", "chef", "chef_effortless"))
41
+ end
42
+
32
43
  # Determine if the node is a docker container.
33
44
  #
34
45
  # @param [Chef::Node] node the node to check
@@ -123,6 +123,21 @@ module ChefUtils
123
123
  # chef-sugar backcompat method
124
124
  alias_method :centos?, :centos_platform?
125
125
 
126
+ # Determine if the current node is CentOS Stream.
127
+ #
128
+ # @param [Chef::Node] node the node to check
129
+ # @since 17.0
130
+ #
131
+ # @return [Boolean]
132
+ #
133
+ def centos_stream_platform?(node = __getnode)
134
+ if node["os_release"]
135
+ node.dig("os_release", "name") == "CentOS Stream"
136
+ else
137
+ node.dig("lsb", "id") == "CentOSStream"
138
+ end
139
+ end
140
+
126
141
  # Determine if the current node is Oracle Linux.
127
142
  #
128
143
  # @param [Chef::Node] node the node to check
@@ -58,7 +58,7 @@ module ChefUtils
58
58
  node["kernel"]["product_type"] == "Server"
59
59
  end
60
60
 
61
- # Determine the current Windows NT version. The NT version often differs from the marketing version, but offers a good way to find desktop and server releases that are based on the same codebase. IE: NT 6.3 is Windows 8.1 and Windows 2012 R2.
61
+ # Determine the current Windows NT version. The NT version often differs from the marketing version, but offers a good way to find desktop and server releases that are based on the same codebase. For example NT 6.3 corresponds to Windows 8.1 and Windows 2012 R2.
62
62
  #
63
63
  # @param [Chef::Node] node the node to check
64
64
  # @since 15.8
@@ -70,7 +70,7 @@ module ChefUtils
70
70
  #
71
71
  def __env_path
72
72
  if __transport_connection
73
- __transport_connection.run_command("echo $PATH").stdout || ""
73
+ __transport_connection.run_command("echo $PATH").stdout.chomp || ""
74
74
  else
75
75
  ENV["PATH"] || ""
76
76
  end
@@ -94,6 +94,10 @@ module ChefUtils
94
94
  end
95
95
  end
96
96
 
97
+ unless method_defined?(:regular_reader)
98
+ alias_method :regular_reader, :[]
99
+ end
100
+
97
101
  unless method_defined?(:regular_writer)
98
102
  alias_method :regular_writer, :[]=
99
103
  end
@@ -102,6 +106,11 @@ module ChefUtils
102
106
  alias_method :regular_update, :update
103
107
  end
104
108
 
109
+ # @param key<Object> The key to get.
110
+ def [](key)
111
+ regular_reader(key)
112
+ end
113
+
105
114
  # @param key<Object> The key to set.
106
115
  # @param value<Object>
107
116
  # The value to set the key to.
@@ -112,6 +121,12 @@ module ChefUtils
112
121
  regular_writer(convert_key(key), convert_value(value))
113
122
  end
114
123
 
124
+ # internal API for use by Chef's deep merge cache
125
+ # @api private
126
+ def internal_get(key)
127
+ regular_reader(key)
128
+ end
129
+
115
130
  # internal API for use by Chef's deep merge cache
116
131
  # @api private
117
132
  def internal_set(key, value)
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Copyright:: Copyright (c) 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 "concurrent/executors"
20
+ require "concurrent/future"
21
+ require "singleton" unless defined?(Singleton)
22
+
23
+ module ChefUtils
24
+ #
25
+ # This module contains ruby refinements that adds several methods to the Enumerable
26
+ # class which are useful for parallel processing.
27
+ #
28
+ module ParallelMap
29
+ refine Enumerable do
30
+
31
+ # Enumerates through the collection in parallel using the thread pool provided
32
+ # or the default thread pool. By using the default thread pool this supports
33
+ # recursively calling the method without deadlocking while using a globally
34
+ # fixed number of workers. This method supports lazy collections. It returns
35
+ # synchronously, waiting until all the work is done. Failures are only reported
36
+ # after the collection has executed and only the first exception is raised.
37
+ #
38
+ # (0..).lazy.parallel_map { |i| i*i }.first(5)
39
+ #
40
+ # @return [Array] output results
41
+ #
42
+ def parallel_map(pool: nil)
43
+ return self unless block_given?
44
+
45
+ pool ||= ChefUtils::DefaultThreadPool.instance.pool
46
+
47
+ futures = map do |item|
48
+ future = Concurrent::Future.execute(executor: pool) do
49
+ yield item
50
+ end
51
+ end
52
+
53
+ futures.map(&:value!)
54
+ end
55
+
56
+ # This has the same behavior as parallel_map but returns the enumerator instead of
57
+ # the return values.
58
+ #
59
+ # @return [Enumerable] the enumerable for method chaining
60
+ #
61
+ def parallel_each(pool: nil, &block)
62
+ return self unless block_given?
63
+
64
+ parallel_map(pool: pool, &block)
65
+
66
+ self
67
+ end
68
+
69
+ # The flat_each method is tightly coupled to the usage of parallel_map within the
70
+ # ChefFS implementation. It is not itself a parallel method, but it is used to
71
+ # iterate through the 2nd level of nested structure, which is tied to the nested
72
+ # structures that ChefFS returns.
73
+ #
74
+ # This is different from Enumerable#flat_map because that behaves like map.flatten(1) while
75
+ # this behaves more like flatten(1).each. We need this on an Enumerable, so we have no
76
+ # Enumerable#flatten method to call.
77
+ #
78
+ # [ [ 1, 2 ], [ 3, 4 ] ].flat_each(&block) calls block four times with 1, 2, 3, 4
79
+ #
80
+ # [ [ 1, 2 ], [ 3, 4 ] ].flat_map(&block) calls block twice with [1, 2] and [3,4]
81
+ #
82
+ def flat_each(&block)
83
+ map do |value|
84
+ if value.is_a?(Enumerable)
85
+ value.each(&block)
86
+ else
87
+ yield value
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ # The DefaultThreadPool has a fixed thread size and has no
95
+ # queue of work and the behavior on failure to find a thread is for the
96
+ # caller to run the work. This contract means that the thread pool can
97
+ # be called recursively without deadlocking and while keeping the fixed
98
+ # number of threads (and not exponentially growing the thread pool with
99
+ # the depth of recursion).
100
+ #
101
+ class DefaultThreadPool
102
+ include Singleton
103
+
104
+ DEFAULT_THREAD_SIZE = 10
105
+
106
+ # Size of the thread pool, must be set before getting the thread pool or
107
+ # calling parallel_map/parallel_each. Does not (but could be modified to)
108
+ # support dynamic resizing. To get fully synchronous behavior set this equal to
109
+ # zero rather than one since the caller will get work if the threads are
110
+ # busy.
111
+ #
112
+ # @return [Integer] number of threads
113
+ attr_accessor :threads
114
+
115
+ # Memoizing accessor for the thread pool
116
+ #
117
+ # @return [Concurrent::ThreadPoolExecutor] the thread pool
118
+ def pool
119
+ @pool ||= Concurrent::ThreadPoolExecutor.new(
120
+ min_threads: threads || DEFAULT_THREAD_SIZE,
121
+ max_threads: threads || DEFAULT_THREAD_SIZE,
122
+ max_queue: 0,
123
+ # "synchronous" redefines the 0 in max_queue to mean 'no queue' instead of 'infinite queue'
124
+ # it does not mean synchronous execution (no threads) but synchronous offload to the threads.
125
+ synchronous: true,
126
+ # this prevents deadlocks on recursive parallel usage
127
+ fallback_policy: :caller_runs
128
+ )
129
+ end
130
+ end
131
+ end
@@ -16,5 +16,5 @@
16
16
 
17
17
  module ChefUtils
18
18
  CHEFUTILS_ROOT = File.expand_path("..", __dir__)
19
- VERSION = "16.11.7"
19
+ VERSION = "17.2.29"
20
20
  end
@@ -45,6 +45,10 @@ RSpec.describe ChefUtils::DSL::Cloud do
45
45
  end
46
46
  end
47
47
 
48
+ context "on alibaba" do
49
+ cloud_reports_true_for(:cloud?, :alibaba?, node: { "alibaba" => {}, "cloud" => {} })
50
+ end
51
+
48
52
  context "on ec2" do
49
53
  cloud_reports_true_for(:cloud?, :ec2?, node: { "ec2" => {}, "cloud" => {} })
50
54
  end
@@ -32,6 +32,18 @@ RSpec.describe ChefUtils::DSL::Introspection do
32
32
 
33
33
  let(:test_instance) { IntrospectionTestClass.new(node) }
34
34
 
35
+ context "#effortless?" do
36
+ # FIXME: use a real VividMash for these tests instead of stubbing
37
+ it "is false by default" do
38
+ expect(node).to receive(:read).with("chef_packages", "chef", "chef_effortless").and_return(nil)
39
+ expect(ChefUtils.effortless?(node)).to be false
40
+ end
41
+ it "is true when ohai reports a effortless" do
42
+ expect(node).to receive(:read).with("chef_packages", "chef", "chef_effortless").and_return(true)
43
+ expect(ChefUtils.effortless?(node)).to be true
44
+ end
45
+ end
46
+
35
47
  context "#docker?" do
36
48
  # FIXME: use a real VividMash for these tests instead of stubbing
37
49
  it "is false by default" do
@@ -90,13 +90,13 @@ RSpec.describe ChefUtils::DSL::PlatformFamily do
90
90
  end
91
91
 
92
92
  context "on centos6" do
93
- let(:options) { { platform: "centos", version: "6.10" } }
93
+ let(:options) { { platform: "centos", version: "6" } }
94
94
 
95
95
  pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?, :rhel6?)
96
96
  end
97
97
 
98
98
  context "on centos7" do
99
- let(:options) { { platform: "centos", version: "7.7.1908" } }
99
+ let(:options) { { platform: "centos", version: "7" } }
100
100
 
101
101
  pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?, :rhel7?)
102
102
  end
@@ -108,7 +108,7 @@ RSpec.describe ChefUtils::DSL::PlatformFamily do
108
108
  end
109
109
 
110
110
  context "on clearos7" do
111
- let(:options) { { platform: "clearos", version: "7.4" } }
111
+ let(:options) { { platform: "clearos", version: "7" } }
112
112
 
113
113
  pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?, :rhel7?)
114
114
  end
@@ -156,25 +156,25 @@ RSpec.describe ChefUtils::DSL::PlatformFamily do
156
156
  end
157
157
 
158
158
  context "on oracle6" do
159
- let(:options) { { platform: "oracle", version: "6.10" } }
159
+ let(:options) { { platform: "oracle", version: "6" } }
160
160
 
161
161
  pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?, :rhel6?)
162
162
  end
163
163
 
164
164
  context "on oracle7" do
165
- let(:options) { { platform: "oracle", version: "7.6" } }
165
+ let(:options) { { platform: "oracle", version: "7" } }
166
166
 
167
167
  pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?, :rhel7?)
168
168
  end
169
169
 
170
170
  context "on redhat6" do
171
- let(:options) { { platform: "redhat", version: "6.10" } }
171
+ let(:options) { { platform: "redhat", version: "6" } }
172
172
 
173
173
  pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?, :rhel6?)
174
174
  end
175
175
 
176
176
  context "on redhat7" do
177
- let(:options) { { platform: "redhat", version: "7.6" } }
177
+ let(:options) { { platform: "redhat", version: "7" } }
178
178
 
179
179
  pf_reports_true_for(:rhel?, :rpm_based?, :fedora_derived?, :redhat_based?, :el?, :rhel7?)
180
180
  end
@@ -145,6 +145,20 @@ RSpec.describe ChefUtils::DSL::Platform do
145
145
  platform_reports_true_for(:centos?, :centos_platform?)
146
146
  end
147
147
 
148
+ context "on centos stream w/o os_release" do
149
+ let(:options) { { platform: "centos" } }
150
+ let(:node) { { "platform" => "centos", "platform_version" => "8", "platform_family" => "rhel", "os" => "linux", "lsb" => { "id" => "CentOSStream" }, "os_release" => nil } }
151
+
152
+ platform_reports_true_for(:centos?, :centos_platform?, :centos_stream_platform?)
153
+ end
154
+
155
+ context "on centos stream w/ os_release" do
156
+ let(:options) { { platform: "centos" } }
157
+ let(:node) { { "platform" => "centos", "platform_version" => "8", "platform_family" => "rhel", "os" => "linux", "os_release" => { "name" => "CentOS Stream" } } }
158
+
159
+ platform_reports_true_for(:centos?, :centos_platform?, :centos_stream_platform?)
160
+ end
161
+
148
162
  context "on clearos" do
149
163
  let(:options) { { platform: "clearos" } }
150
164
 
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Copyright:: Copyright (c) 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 "chef-utils/parallel_map"
20
+
21
+ using ChefUtils::ParallelMap
22
+
23
+ RSpec.describe ChefUtils::ParallelMap do
24
+
25
+ shared_examples_for "common parallel API tests" do
26
+
27
+ before(:each) do
28
+ ChefUtils::DefaultThreadPool.instance.instance_variable_set(:@pool, nil)
29
+ ChefUtils::DefaultThreadPool.instance.threads = threads
30
+ end
31
+
32
+ after(:each) do
33
+ ChefUtils::DefaultThreadPool.instance.instance_variable_set(:@pool, nil)
34
+ end
35
+
36
+ it "parallel_map runs in parallel" do
37
+ # this is implicitly also testing that we run in the caller when we exhaust threads by running threads+1
38
+ val = threads + 1
39
+ ret = []
40
+ start = Time.now
41
+ (1..val).parallel_map do |i|
42
+ loop do
43
+ if val == i
44
+ ret << i
45
+ val -= 1
46
+ break
47
+ end
48
+ # we spin for quite awhile to wait for very slow testers if we have to
49
+ if Time.now - start > 30
50
+ raise "timed out; deadlocked due to lack of parallelization?"
51
+ end
52
+
53
+ # need to sleep a tiny bit to let other threads schedule
54
+ sleep 0.000001
55
+ end
56
+ end
57
+ expected = (1..threads + 1).to_a.reverse
58
+ expect(ret).to eql(expected)
59
+ end
60
+
61
+ it "parallel_each runs in parallel" do
62
+ # this is implicitly also testing that we run in the caller when we exhaust threads by running threads+1
63
+ val = threads + 1
64
+ ret = []
65
+ start = Time.now
66
+ (1..val).parallel_each do |i|
67
+ loop do
68
+ if val == i
69
+ ret << i
70
+ val -= 1
71
+ break
72
+ end
73
+ # we spin for quite awhile to wait for very slow testers if we have to
74
+ if Time.now - start > 30
75
+ raise "timed out; deadlocked due to lack of parallelization?"
76
+ end
77
+
78
+ # need to sleep a tiny bit to let other threads schedule
79
+ sleep 0.000001
80
+ end
81
+ end
82
+ expected = (1..threads + 1).to_a.reverse
83
+ expect(ret).to eql(expected)
84
+ end
85
+
86
+ it "parallel_map throws exceptions" do
87
+ expect { (0..10).parallel_map { |i| raise "boom" } }.to raise_error(RuntimeError)
88
+ end
89
+
90
+ it "parallel_each throws exceptions" do
91
+ expect { (0..10).parallel_each { |i| raise "boom" } }.to raise_error(RuntimeError)
92
+ end
93
+
94
+ it "parallel_map runs" do
95
+ ans = Timeout.timeout(30) do
96
+ (1..10).parallel_map { |i| i }
97
+ end
98
+ expect(ans).to eql((1..10).to_a)
99
+ end
100
+
101
+ it "parallel_each runs" do
102
+ Timeout.timeout(30) do
103
+ (1..10).parallel_each { |i| i }
104
+ end
105
+ end
106
+
107
+ it "recursive parallel_map will not deadlock" do
108
+ ans = Timeout.timeout(30) do
109
+ (1..2).parallel_map { |i| (1..2).parallel_map { |i| i } }
110
+ end
111
+ expect(ans).to eql([[1, 2], [1, 2]])
112
+ end
113
+
114
+ it "recursive parallel_each will not deadlock" do
115
+ ans = Timeout.timeout(30) do
116
+ (1..2).parallel_each { |i| (1..2).parallel_each { |i| i } }
117
+ end
118
+ end
119
+
120
+ it "parallel_map is lazy" do
121
+ ans = Timeout.timeout(30) do
122
+ (1..).lazy.parallel_map { |i| i }.first(5)
123
+ end
124
+ expect(ans).to eql((1..5).to_a)
125
+ end
126
+
127
+ it "parallel_each is lazy" do
128
+ ans = Timeout.timeout(30) do
129
+ (1..).lazy.parallel_each { |i| i }.first(5)
130
+ end
131
+ end
132
+ end
133
+
134
+ context "with 10 threads" do
135
+ let(:threads) { 10 }
136
+ it_behaves_like "common parallel API tests"
137
+ end
138
+
139
+ context "with 0 threads" do
140
+ let(:threads) { 0 }
141
+ it_behaves_like "common parallel API tests"
142
+ end
143
+
144
+ context "with 1 threads" do
145
+ let(:threads) { 1 }
146
+ it_behaves_like "common parallel API tests"
147
+ end
148
+
149
+ context "flat_each" do
150
+ it "runs each over items which are nested one level" do
151
+ sum = 0
152
+ [ [ 1, 2 ], [3, 4]].flat_each { |i| sum += i }
153
+ expect(sum).to eql(10)
154
+ end
155
+ end
156
+ end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chef-utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 16.11.7
4
+ version: 17.2.29
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chef Software, Inc
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-17 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2021-06-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: concurrent-ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description:
14
28
  email:
15
29
  - oss@chef.io
@@ -38,6 +52,7 @@ files:
38
52
  - lib/chef-utils/dsl/windows.rb
39
53
  - lib/chef-utils/internal.rb
40
54
  - lib/chef-utils/mash.rb
55
+ - lib/chef-utils/parallel_map.rb
41
56
  - lib/chef-utils/version.rb
42
57
  - lib/chef-utils/version_string.rb
43
58
  - spec/spec_helper.rb
@@ -54,6 +69,7 @@ files:
54
69
  - spec/unit/dsl/which_spec.rb
55
70
  - spec/unit/dsl/windows_spec.rb
56
71
  - spec/unit/mash_spec.rb
72
+ - spec/unit/parallel_map_spec.rb
57
73
  homepage: https://github.com/chef/chef/tree/master/chef-utils
58
74
  licenses:
59
75
  - Apache-2.0
@@ -71,14 +87,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
71
87
  requirements:
72
88
  - - ">="
73
89
  - !ruby/object:Gem::Version
74
- version: 2.6.0
90
+ version: '2.6'
75
91
  required_rubygems_version: !ruby/object:Gem::Requirement
76
92
  requirements:
77
93
  - - ">="
78
94
  - !ruby/object:Gem::Version
79
95
  version: '0'
80
96
  requirements: []
81
- rubygems_version: 3.1.4
97
+ rubygems_version: 3.2.15
82
98
  signing_key:
83
99
  specification_version: 4
84
100
  summary: Basic utility functions for Core Chef Infra development