chef-utils 16.10.17 → 17.1.35

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 62f211d198ca5e3ab4a7e86a33bb5679f78651ff3f94364046a284799bdac6ff
4
- data.tar.gz: ee619f1dcbb20f47caf2e366bef6d552cc747d3a88bd6b2ed85dff4f3d788ee1
3
+ metadata.gz: 3adf9958a3e25602085a26528f12a34d96fbb14e7951c3793fa4a743304f474b
4
+ data.tar.gz: e9723727db0a47455af03f4f3e78437221a94ab35495e2c8ababb16678a21113
5
5
  SHA512:
6
- metadata.gz: eb5ee47a673cc987a2e3d2bff07b1a8a376acd66f38f42add836375407509166bec45dec0a36595048a3d59e71dbd3e62cb698413a12bd26add01ba8d5bfe237
7
- data.tar.gz: 9cf15493a995943360b3b367639f0e6cd197ca299d07a79ad2a769943ace4674b32c04345544db46640e257af2bfa0944cc3db35b7ea34f391ff4a610c95d63a
6
+ metadata.gz: '028316241e1f4b2e1dedd76b9b84590db98ddbe047fd163fef094ab6eee35503f34df805a282b2f508bbb001c9abcc84533d918f28af22adb3e4e75849b7ccfa'
7
+ data.tar.gz: 539833b147fb391df7dea4ec3b0dc297b19fcb3ef6a68df44bad666fa46767e6ce64f1daabc79704cacf3822d72788afe9ab27787ba398a964b006fb8a398d24
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
@@ -87,6 +87,11 @@ module ChefUtils
87
87
  EXEC = "chef-solo"
88
88
  end
89
89
 
90
+ class Workstation
91
+ # The suffix for Chef Workstation's /opt/chef-workstation or C:\\opscode\chef-workstation
92
+ DIR_SUFFIX = "chef-workstation"
93
+ end
94
+
90
95
  class Zero
91
96
  # chef-zero executable
92
97
  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.10.17"
19
+ VERSION = "17.1.35"
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.10.17
4
+ version: 17.1.35
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-02-19 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2021-05-11 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