chef-zero 15.1.0 → 15.1.11

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: 00071a8eebf554379bd831d228ca87964ee665a935232b1055ab0ab9628117d4
4
- data.tar.gz: 15c1ee1fbe857a55ee344ecd1329b786731bc7526cb0d4b56d6801595e7e3520
3
+ metadata.gz: deb326897728f384377d741e312e4c513918d1d3e6270106a46dc267f830924d
4
+ data.tar.gz: b22286cf8ea5b081b3242f70610e38d63fa8bb4bf8fd2c18a0d62299c93e98ca
5
5
  SHA512:
6
- metadata.gz: 0e35acb20599a35cf00e7475172c6596b898f5e2597a12421a2f01d50d84afd4431157ec6f6d41e6f9854d6a912f1bd94d6ea559d941e3a54d6d39cbab5c2108
7
- data.tar.gz: cb64bb7aa2be0a339c2508c06355f2b45bcdfdb2d3fef7ffa50d5ad2ed0a85de3381c80039d14fea7a1d5fbc00891c60cc095dfd01ecb64dba4565d2c8c8fe5c
6
+ metadata.gz: 6e51ac4dad86f587a860231dfdf204db27aed48d9c3fd75be4467580b3d8fac913bffa650b6f9ce88be16484dce81fa861dc5405ccde6e385f34c380971e8e3a
7
+ data.tar.gz: e1be28ae5469a3538c28172e904aa1391aba8a3f83665c8d44e41eb23e4af0821f5023d89f3f8ab61993095f3f147e0950f82f9d4bf85ac96c83d219eb3b9f0c
data/Gemfile CHANGED
@@ -18,7 +18,7 @@ group :development, :test do
18
18
  end
19
19
 
20
20
  group :style do
21
- gem "cookstyle", "~> 8.2"
21
+ gem "cookstyle", "~> 8.6"
22
22
  end
23
23
 
24
24
  if ENV["GEMFILE_MOD"]
data/chef-zero.gemspec CHANGED
@@ -15,11 +15,10 @@ Gem::Specification.new do |s|
15
15
 
16
16
  s.add_dependency "mixlib-log", ">= 2.0", "< 4.0"
17
17
  s.add_dependency "hashie", ">= 2.0", "< 6.0"
18
- s.add_dependency "uuidtools", "~> 2.1"
18
+ s.add_dependency "uuidtools", ">= 2.1", "< 4.0"
19
19
  s.add_dependency "ffi-yajl", ">= 2.2", "< 4.0"
20
- s.add_dependency "rack", "~> 3.1", ">= 3.1.16"
21
- s.add_dependency "rackup", "~> 2.2", ">= 2.2.1"
22
- s.add_dependency "unf_ext", "~> 0.0.8"
20
+ s.add_dependency "rack", "~> 3.2", ">= 3.2.6"
21
+ s.add_dependency "rackup", "~> 2.3", ">= 2.3.1"
23
22
  s.add_dependency "webrick"
24
23
 
25
24
  s.bindir = "bin"
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: John Keiser (<jkeiser@opscode.com>)
3
- # Copyright:: Copyright (c) 2013 Opscode, Inc.
3
+ # Copyright:: Copyright (c) 2012-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: John Keiser (<jkeiser@opscode.com>)
3
- # Copyright:: Copyright (c) 2013 Opscode, Inc.
3
+ # Copyright:: Copyright (c) 2012-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: John Keiser (<jkeiser@opscode.com>)
3
- # Copyright:: Copyright (c) 2013 Opscode, Inc.
3
+ # Copyright:: Copyright (c) 2012-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: John Keiser (<jkeiser@opscode.com>)
3
- # Copyright:: Copyright (c) 2013 Opscode, Inc.
3
+ # Copyright:: Copyright (c) 2012-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: John Keiser (<jkeiser@opscode.com>)
3
- # Copyright:: Copyright (c) 2013 Opscode, Inc.
3
+ # Copyright:: Copyright (c) 2012-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: John Keiser (<jkeiser@opscode.com>)
3
- # Copyright:: Copyright (c) 2013 Opscode, Inc.
3
+ # Copyright:: Copyright (c) 2012-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: John Keiser (<jkeiser@opscode.com>)
3
- # Copyright:: Copyright (c) 2014 Opscode, Inc.
3
+ # Copyright:: Copyright (c) 2012-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -84,7 +84,8 @@ module ChefZero
84
84
  new_unsolved = unsolved[1..-1]
85
85
 
86
86
  # Pick this cookbook, and add dependencies
87
- cookbook_obj = FFI_Yajl::Parser.parse(get_data(request, request.rest_path[0..1] + ["cookbooks", solve_for, desired_version]))
87
+ cookbook_obj = cookbook_obj_cache.dig(solve_for, desired_version) ||
88
+ FFI_Yajl::Parser.parse(get_data(request, request.rest_path[0..1] + ["cookbooks", solve_for, desired_version]))
88
89
  cookbook_obj_cache[solve_for] ||= {}
89
90
  cookbook_obj_cache[solve_for][desired_version] = cookbook_obj
90
91
  cookbook_metadata = cookbook_obj["metadata"] || {}
@@ -110,7 +111,8 @@ module ChefZero
110
111
  next if dep_not_found
111
112
 
112
113
  # Depsolve children with this desired version! First solution wins.
113
- result, cookbook_obj_cache = depsolve(request, new_unsolved, new_desired_versions, environment_constraints, cookbook_obj_cache)
114
+ result, returned_cache = depsolve(request, new_unsolved, new_desired_versions, environment_constraints, cookbook_obj_cache)
115
+ cookbook_obj_cache = returned_cache || cookbook_obj_cache
114
116
  return [result, cookbook_obj_cache] if result
115
117
  end
116
118
  [nil, nil]
@@ -118,7 +120,8 @@ module ChefZero
118
120
 
119
121
  def sort_versions(versions)
120
122
  result = versions.sort_by { |version| Gem::Version.new(version.dup) }
121
- result.reverse
123
+ result.reverse!
124
+ result
122
125
  end
123
126
 
124
127
  def filter_by_constraint(versions, cookbook_name, constraint)
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: John Keiser (<jkeiser@opscode.com>)
3
- # Copyright:: Copyright (c) 2012-2017, Chef Software Inc.
3
+ # Copyright:: Copyright (c) 2012-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: Daniel DeLeo (<dan@chef.io>)
3
- # Copyright:: Copyright (c) 2012 Opscode, Inc.
3
+ # Copyright:: Copyright (c) 2012-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,3 +1,3 @@
1
1
  module ChefZero
2
- VERSION = "15.1.0".freeze
2
+ VERSION = "15.1.11".freeze
3
3
  end
@@ -0,0 +1,436 @@
1
+ require "uri" unless defined?(URI)
2
+ require "chef_zero/endpoints/environment_cookbook_versions_endpoint"
3
+
4
+ describe ChefZero::Endpoints::EnvironmentCookbookVersionsEndpoint do
5
+ let(:server) { double("server") }
6
+ let(:endpoint) { described_class.new(server) }
7
+
8
+ describe "#sort_versions" do
9
+ it "returns versions sorted descending by semver" do
10
+ versions = ["1.0.0", "2.0.0", "1.5.0", "0.9.0"]
11
+ expect(endpoint.sort_versions(versions)).to eq(["2.0.0", "1.5.0", "1.0.0", "0.9.0"])
12
+ end
13
+
14
+ it "handles single-element lists" do
15
+ expect(endpoint.sort_versions(["1.0.0"])).to eq(["1.0.0"])
16
+ end
17
+
18
+ it "handles an empty list" do
19
+ expect(endpoint.sort_versions([])).to eq([])
20
+ end
21
+
22
+ it "sorts multi-digit version components numerically" do
23
+ versions = ["1.2.3", "1.10.0", "1.9.0"]
24
+ expect(endpoint.sort_versions(versions)).to eq(["1.10.0", "1.9.0", "1.2.3"])
25
+ end
26
+ end
27
+
28
+ describe "#filter_by_constraint" do
29
+ let(:versions) { { "apache" => ["1.0.0", "1.5.0", "2.0.0", "2.1.0"] } }
30
+
31
+ it "returns versions unchanged when constraint is nil" do
32
+ result = endpoint.filter_by_constraint(versions, "apache", nil)
33
+ expect(result["apache"]).to eq(["1.0.0", "1.5.0", "2.0.0", "2.1.0"])
34
+ end
35
+
36
+ it "filters with an exact version constraint" do
37
+ result = endpoint.filter_by_constraint(versions, "apache", "= 1.5.0")
38
+ expect(result["apache"]).to eq(["1.5.0"])
39
+ end
40
+
41
+ it "filters with a >= constraint" do
42
+ result = endpoint.filter_by_constraint(versions, "apache", ">= 2.0.0")
43
+ expect(result["apache"]).to eq(["2.0.0", "2.1.0"])
44
+ end
45
+
46
+ it "filters with a ~> constraint" do
47
+ result = endpoint.filter_by_constraint(versions, "apache", "~> 1.0")
48
+ expect(result["apache"]).to eq(["1.0.0", "1.5.0"])
49
+ end
50
+
51
+ it "returns empty array when no versions match" do
52
+ result = endpoint.filter_by_constraint(versions, "apache", "= 9.9.9")
53
+ expect(result["apache"]).to eq([])
54
+ end
55
+
56
+ it "does not mutate the original versions hash" do
57
+ original = versions.dup
58
+ endpoint.filter_by_constraint(versions, "apache", "= 1.0.0")
59
+ expect(versions).to eq(original)
60
+ end
61
+ end
62
+
63
+ describe "#depsolve" do
64
+ let(:org_prefix) { %w{organizations testorg} }
65
+ let(:request) { double("request", rest_path: org_prefix + %w{environments _default cookbook_versions}) }
66
+ let(:data_store) { double("data_store") }
67
+
68
+ before do
69
+ allow(server).to receive(:data_store).and_return(data_store)
70
+ end
71
+
72
+ def cookbook_json(name, version, dependencies = {})
73
+ FFI_Yajl::Encoder.encode({
74
+ "metadata" => {
75
+ "name" => name,
76
+ "version" => version,
77
+ "dependencies" => dependencies,
78
+ },
79
+ })
80
+ end
81
+
82
+ context "base cases" do
83
+ it "returns [nil, nil] when a cookbook has empty versions" do
84
+ desired = { "apache" => [] }
85
+ result, _cache = endpoint.depsolve(request, ["apache"], desired, {})
86
+ expect(result).to be_nil
87
+ end
88
+
89
+ it "sets @last_constraint_failure when a cookbook has empty versions" do
90
+ desired = { "apache" => [] }
91
+ endpoint.depsolve(request, ["apache"], desired, {})
92
+ expect(endpoint.instance_variable_get(:@last_constraint_failure)).to eq("apache")
93
+ end
94
+
95
+ it "returns desired_versions when unsolved list is empty" do
96
+ desired = { "apache" => ["1.0.0"] }
97
+ result, _cache = endpoint.depsolve(request, [], desired, {})
98
+ expect(result).to eq(desired)
99
+ end
100
+
101
+ it "resolves a cookbook with no metadata key" do
102
+ desired = { "minimal" => ["1.0.0"] }
103
+ allow(data_store).to receive(:get)
104
+ .with(org_prefix + ["cookbooks", "minimal", "1.0.0"], request)
105
+ .and_return(FFI_Yajl::Encoder.encode({}))
106
+
107
+ result, cache = endpoint.depsolve(request, ["minimal"], desired, {})
108
+ expect(result["minimal"]).to eq(["1.0.0"])
109
+ expect(cache["minimal"]).to have_key("1.0.0")
110
+ end
111
+ end
112
+
113
+ context "simple resolution" do
114
+ it "resolves a single cookbook with no dependencies" do
115
+ desired = { "apache" => ["1.0.0", "2.0.0"] }
116
+ allow(data_store).to receive(:get)
117
+ .with(org_prefix + ["cookbooks", "apache", "2.0.0"], request)
118
+ .and_return(cookbook_json("apache", "2.0.0"))
119
+ allow(data_store).to receive(:get)
120
+ .with(org_prefix + ["cookbooks", "apache", "1.0.0"], request)
121
+ .and_return(cookbook_json("apache", "1.0.0"))
122
+
123
+ result, cache = endpoint.depsolve(request, ["apache"], desired, {})
124
+ expect(result["apache"]).to eq(["2.0.0"])
125
+ expect(cache["apache"]["2.0.0"]).to be_a(Hash)
126
+ end
127
+
128
+ it "resolves a cookbook with a satisfiable dependency" do
129
+ desired = { "apache" => ["1.0.0"] }
130
+ allow(data_store).to receive(:get)
131
+ .with(org_prefix + ["cookbooks", "apache", "1.0.0"], request)
132
+ .and_return(cookbook_json("apache", "1.0.0", { "mysql" => ">= 1.0.0" }))
133
+ allow(data_store).to receive(:exists_dir?)
134
+ .with(org_prefix + %w{cookbooks mysql})
135
+ .and_return(true)
136
+ allow(data_store).to receive(:list)
137
+ .with(org_prefix + %w{cookbooks mysql})
138
+ .and_return(["1.0.0", "2.0.0"])
139
+ allow(data_store).to receive(:get)
140
+ .with(org_prefix + ["cookbooks", "mysql", "2.0.0"], request)
141
+ .and_return(cookbook_json("mysql", "2.0.0"))
142
+ allow(data_store).to receive(:get)
143
+ .with(org_prefix + ["cookbooks", "mysql", "1.0.0"], request)
144
+ .and_return(cookbook_json("mysql", "1.0.0"))
145
+
146
+ result, _cache = endpoint.depsolve(request, ["apache"], desired, {})
147
+ expect(result["apache"]).to eq(["1.0.0"])
148
+ expect(result["mysql"]).to eq(["2.0.0"])
149
+ end
150
+
151
+ it "returns [nil, nil] when a dependency cookbook does not exist" do
152
+ desired = { "apache" => ["1.0.0"] }
153
+ allow(data_store).to receive(:get)
154
+ .with(org_prefix + ["cookbooks", "apache", "1.0.0"], request)
155
+ .and_return(cookbook_json("apache", "1.0.0", { "missing" => ">= 0.0.0" }))
156
+ allow(data_store).to receive(:exists_dir?)
157
+ .with(org_prefix + %w{cookbooks missing})
158
+ .and_return(false)
159
+
160
+ result, _cache = endpoint.depsolve(request, ["apache"], desired, {})
161
+ expect(result).to be_nil
162
+ expect(endpoint.instance_variable_get(:@last_missing_dep)).to eq("missing")
163
+ end
164
+ end
165
+
166
+ context "constraint filtering" do
167
+ it "applies environment constraints to dependency versions" do
168
+ desired = { "apache" => ["1.0.0"] }
169
+ env_constraints = { "mysql" => "= 1.0.0" }
170
+
171
+ allow(data_store).to receive(:get)
172
+ .with(org_prefix + ["cookbooks", "apache", "1.0.0"], request)
173
+ .and_return(cookbook_json("apache", "1.0.0", { "mysql" => ">= 0.0.0" }))
174
+ allow(data_store).to receive(:exists_dir?)
175
+ .with(org_prefix + %w{cookbooks mysql})
176
+ .and_return(true)
177
+ allow(data_store).to receive(:list)
178
+ .with(org_prefix + %w{cookbooks mysql})
179
+ .and_return(["1.0.0", "2.0.0"])
180
+ allow(data_store).to receive(:get)
181
+ .with(org_prefix + ["cookbooks", "mysql", "1.0.0"], request)
182
+ .and_return(cookbook_json("mysql", "1.0.0"))
183
+
184
+ result, _cache = endpoint.depsolve(request, ["apache"], desired, env_constraints)
185
+ expect(result["mysql"]).to eq(["1.0.0"])
186
+ end
187
+
188
+ it "filters dependency versions by the dependency's own constraint" do
189
+ desired = { "apache" => ["1.0.0"] }
190
+ allow(data_store).to receive(:get)
191
+ .with(org_prefix + ["cookbooks", "apache", "1.0.0"], request)
192
+ .and_return(cookbook_json("apache", "1.0.0", { "mysql" => ">= 2.0.0" }))
193
+ allow(data_store).to receive(:exists_dir?)
194
+ .with(org_prefix + %w{cookbooks mysql})
195
+ .and_return(true)
196
+ allow(data_store).to receive(:list)
197
+ .with(org_prefix + %w{cookbooks mysql})
198
+ .and_return(["1.0.0", "2.0.0"])
199
+ allow(data_store).to receive(:get)
200
+ .with(org_prefix + ["cookbooks", "mysql", "2.0.0"], request)
201
+ .and_return(cookbook_json("mysql", "2.0.0"))
202
+
203
+ result, _cache = endpoint.depsolve(request, ["apache"], desired, {})
204
+ expect(result["mysql"]).to eq(["2.0.0"])
205
+ end
206
+
207
+ it "returns nil when dep constraint filters out all available versions" do
208
+ desired = { "apache" => ["1.0.0"] }
209
+ allow(data_store).to receive(:get)
210
+ .with(org_prefix + ["cookbooks", "apache", "1.0.0"], request)
211
+ .and_return(cookbook_json("apache", "1.0.0", { "mysql" => ">= 5.0.0" }))
212
+ allow(data_store).to receive(:exists_dir?)
213
+ .with(org_prefix + %w{cookbooks mysql})
214
+ .and_return(true)
215
+ allow(data_store).to receive(:list)
216
+ .with(org_prefix + %w{cookbooks mysql})
217
+ .and_return(["1.0.0", "2.0.0"])
218
+
219
+ result, _cache = endpoint.depsolve(request, ["apache"], desired, {})
220
+ expect(result).to be_nil
221
+ end
222
+ end
223
+
224
+ context "backtracking" do
225
+ it "falls back to an older version when newest has unsatisfiable dep" do
226
+ desired = { "web" => ["1.0.0", "2.0.0"] }
227
+ # v2.0.0 depends on "ghost" which doesn't exist
228
+ allow(data_store).to receive(:get)
229
+ .with(org_prefix + ["cookbooks", "web", "2.0.0"], request)
230
+ .and_return(cookbook_json("web", "2.0.0", { "ghost" => ">= 0.0.0" }))
231
+ allow(data_store).to receive(:exists_dir?)
232
+ .with(org_prefix + %w{cookbooks ghost})
233
+ .and_return(false)
234
+ # v1.0.0 has no deps
235
+ allow(data_store).to receive(:get)
236
+ .with(org_prefix + ["cookbooks", "web", "1.0.0"], request)
237
+ .and_return(cookbook_json("web", "1.0.0"))
238
+
239
+ result, _cache = endpoint.depsolve(request, ["web"], desired, {})
240
+ expect(result["web"]).to eq(["1.0.0"])
241
+ end
242
+
243
+ it "backtracks through multiple failing versions" do
244
+ desired = { "web" => ["1.0.0", "2.0.0", "3.0.0"] }
245
+ allow(data_store).to receive(:get)
246
+ .with(org_prefix + ["cookbooks", "web", "3.0.0"], request)
247
+ .and_return(cookbook_json("web", "3.0.0", { "missing_a" => ">= 0.0.0" }))
248
+ allow(data_store).to receive(:exists_dir?)
249
+ .with(org_prefix + %w{cookbooks missing_a})
250
+ .and_return(false)
251
+ allow(data_store).to receive(:get)
252
+ .with(org_prefix + ["cookbooks", "web", "2.0.0"], request)
253
+ .and_return(cookbook_json("web", "2.0.0", { "missing_b" => ">= 0.0.0" }))
254
+ allow(data_store).to receive(:exists_dir?)
255
+ .with(org_prefix + %w{cookbooks missing_b})
256
+ .and_return(false)
257
+ allow(data_store).to receive(:get)
258
+ .with(org_prefix + ["cookbooks", "web", "1.0.0"], request)
259
+ .and_return(cookbook_json("web", "1.0.0"))
260
+
261
+ result, _cache = endpoint.depsolve(request, ["web"], desired, {})
262
+ expect(result["web"]).to eq(["1.0.0"])
263
+ end
264
+ end
265
+
266
+ context "multiple cookbooks and dependencies" do
267
+ it "populates cache entries for all resolved cookbooks" do
268
+ desired = { "apache" => ["1.0.0"] }
269
+ allow(data_store).to receive(:get)
270
+ .with(org_prefix + ["cookbooks", "apache", "1.0.0"], request)
271
+ .and_return(cookbook_json("apache", "1.0.0", { "mysql" => ">= 1.0.0" }))
272
+ allow(data_store).to receive(:exists_dir?)
273
+ .with(org_prefix + %w{cookbooks mysql})
274
+ .and_return(true)
275
+ allow(data_store).to receive(:list)
276
+ .with(org_prefix + %w{cookbooks mysql})
277
+ .and_return(["1.0.0"])
278
+ allow(data_store).to receive(:get)
279
+ .with(org_prefix + ["cookbooks", "mysql", "1.0.0"], request)
280
+ .and_return(cookbook_json("mysql", "1.0.0"))
281
+
282
+ _result, cache = endpoint.depsolve(request, ["apache"], desired, {})
283
+ expect(cache["apache"]).to have_key("1.0.0")
284
+ expect(cache["apache"]["1.0.0"]["metadata"]["name"]).to eq("apache")
285
+ expect(cache["mysql"]).to have_key("1.0.0")
286
+ expect(cache["mysql"]["1.0.0"]["metadata"]["name"]).to eq("mysql")
287
+ end
288
+
289
+ it "resolves multiple independent unsolved cookbooks" do
290
+ desired = {
291
+ "apache" => ["1.0.0", "2.0.0"],
292
+ "nginx" => ["3.0.0", "4.0.0"],
293
+ }
294
+ allow(data_store).to receive(:get)
295
+ .with(org_prefix + ["cookbooks", "apache", "2.0.0"], request)
296
+ .and_return(cookbook_json("apache", "2.0.0"))
297
+ allow(data_store).to receive(:get)
298
+ .with(org_prefix + ["cookbooks", "apache", "1.0.0"], request)
299
+ .and_return(cookbook_json("apache", "1.0.0"))
300
+ allow(data_store).to receive(:get)
301
+ .with(org_prefix + ["cookbooks", "nginx", "4.0.0"], request)
302
+ .and_return(cookbook_json("nginx", "4.0.0"))
303
+ allow(data_store).to receive(:get)
304
+ .with(org_prefix + ["cookbooks", "nginx", "3.0.0"], request)
305
+ .and_return(cookbook_json("nginx", "3.0.0"))
306
+
307
+ result, cache = endpoint.depsolve(request, %w{apache nginx}, desired, {})
308
+ expect(result["apache"]).to eq(["2.0.0"])
309
+ expect(result["nginx"]).to eq(["4.0.0"])
310
+ expect(cache.keys).to contain_exactly("apache", "nginx")
311
+ end
312
+
313
+ it "resolves a diamond dependency (two cookbooks sharing a dep)" do
314
+ desired = {
315
+ "apache" => ["1.0.0"],
316
+ "nginx" => ["1.0.0"],
317
+ }
318
+ allow(data_store).to receive(:get)
319
+ .with(org_prefix + ["cookbooks", "apache", "1.0.0"], request)
320
+ .and_return(cookbook_json("apache", "1.0.0", { "mysql" => ">= 1.0.0" }))
321
+ allow(data_store).to receive(:get)
322
+ .with(org_prefix + ["cookbooks", "nginx", "1.0.0"], request)
323
+ .and_return(cookbook_json("nginx", "1.0.0", { "mysql" => ">= 2.0.0" }))
324
+ allow(data_store).to receive(:exists_dir?)
325
+ .with(org_prefix + %w{cookbooks mysql})
326
+ .and_return(true)
327
+ allow(data_store).to receive(:list)
328
+ .with(org_prefix + %w{cookbooks mysql})
329
+ .and_return(["1.0.0", "2.0.0", "3.0.0"])
330
+ allow(data_store).to receive(:get)
331
+ .with(org_prefix + ["cookbooks", "mysql", "3.0.0"], request)
332
+ .and_return(cookbook_json("mysql", "3.0.0"))
333
+ allow(data_store).to receive(:get)
334
+ .with(org_prefix + ["cookbooks", "mysql", "2.0.0"], request)
335
+ .and_return(cookbook_json("mysql", "2.0.0"))
336
+
337
+ result, _cache = endpoint.depsolve(request, %w{apache nginx}, desired, {})
338
+ expect(result["apache"]).to eq(["1.0.0"])
339
+ expect(result["nginx"]).to eq(["1.0.0"])
340
+ expect(result["mysql"]).to eq(["3.0.0"])
341
+ end
342
+
343
+ it "returns nil when dependency constraints from two cookbooks conflict" do
344
+ desired = {
345
+ "apache" => ["1.0.0"],
346
+ "nginx" => ["1.0.0"],
347
+ }
348
+ allow(data_store).to receive(:get)
349
+ .with(org_prefix + ["cookbooks", "apache", "1.0.0"], request)
350
+ .and_return(cookbook_json("apache", "1.0.0", { "mysql" => ">= 2.0.0" }))
351
+ allow(data_store).to receive(:get)
352
+ .with(org_prefix + ["cookbooks", "nginx", "1.0.0"], request)
353
+ .and_return(cookbook_json("nginx", "1.0.0", { "mysql" => "< 2.0.0" }))
354
+ allow(data_store).to receive(:exists_dir?)
355
+ .with(org_prefix + %w{cookbooks mysql})
356
+ .and_return(true)
357
+ allow(data_store).to receive(:list)
358
+ .with(org_prefix + %w{cookbooks mysql})
359
+ .and_return(["1.0.0", "2.0.0"])
360
+
361
+ result, _cache = endpoint.depsolve(request, %w{apache nginx}, desired, {})
362
+ expect(result).to be_nil
363
+ end
364
+
365
+ it "uses cached cookbook data instead of re-fetching during backtracking" do
366
+ desired = {
367
+ "web" => ["1.0.0", "2.0.0"],
368
+ "db" => ["1.0.0"],
369
+ }
370
+ # web 2.0.0 depends on lib, which depends on missing (doesn't exist) → branch fails
371
+ # web 1.0.0 has no deps → backtracks here, db must be re-solved
372
+ # db 1.0.0 should be fetched only once (cached from the first branch)
373
+ allow(data_store).to receive(:get)
374
+ .with(org_prefix + ["cookbooks", "web", "2.0.0"], request)
375
+ .and_return(cookbook_json("web", "2.0.0", { "lib" => ">= 1.0.0" }))
376
+ allow(data_store).to receive(:get)
377
+ .with(org_prefix + ["cookbooks", "web", "1.0.0"], request)
378
+ .and_return(cookbook_json("web", "1.0.0"))
379
+ expect(data_store).to receive(:get)
380
+ .with(org_prefix + ["cookbooks", "db", "1.0.0"], request)
381
+ .once
382
+ .and_return(cookbook_json("db", "1.0.0"))
383
+ allow(data_store).to receive(:exists_dir?)
384
+ .with(org_prefix + %w{cookbooks lib})
385
+ .and_return(true)
386
+ allow(data_store).to receive(:list)
387
+ .with(org_prefix + %w{cookbooks lib})
388
+ .and_return(["1.0.0"])
389
+ allow(data_store).to receive(:get)
390
+ .with(org_prefix + ["cookbooks", "lib", "1.0.0"], request)
391
+ .and_return(cookbook_json("lib", "1.0.0", { "missing" => ">= 1.0.0" }))
392
+ allow(data_store).to receive(:exists_dir?)
393
+ .with(org_prefix + %w{cookbooks missing})
394
+ .and_return(false)
395
+
396
+ result, _cache = endpoint.depsolve(request, %w{web db}, desired, {})
397
+ expect(result["web"]).to eq(["1.0.0"])
398
+ expect(result["db"]).to eq(["1.0.0"])
399
+ end
400
+
401
+ it "resolves a deep dependency chain" do
402
+ desired = { "app" => ["1.0.0"] }
403
+ allow(data_store).to receive(:get)
404
+ .with(org_prefix + ["cookbooks", "app", "1.0.0"], request)
405
+ .and_return(cookbook_json("app", "1.0.0", { "framework" => ">= 1.0.0" }))
406
+ allow(data_store).to receive(:exists_dir?)
407
+ .with(org_prefix + %w{cookbooks framework})
408
+ .and_return(true)
409
+ allow(data_store).to receive(:list)
410
+ .with(org_prefix + %w{cookbooks framework})
411
+ .and_return(["1.0.0"])
412
+ allow(data_store).to receive(:get)
413
+ .with(org_prefix + ["cookbooks", "framework", "1.0.0"], request)
414
+ .and_return(cookbook_json("framework", "1.0.0", { "lib" => ">= 1.0.0" }))
415
+ allow(data_store).to receive(:exists_dir?)
416
+ .with(org_prefix + %w{cookbooks lib})
417
+ .and_return(true)
418
+ allow(data_store).to receive(:list)
419
+ .with(org_prefix + %w{cookbooks lib})
420
+ .and_return(["1.0.0", "2.0.0"])
421
+ allow(data_store).to receive(:get)
422
+ .with(org_prefix + ["cookbooks", "lib", "2.0.0"], request)
423
+ .and_return(cookbook_json("lib", "2.0.0"))
424
+ allow(data_store).to receive(:get)
425
+ .with(org_prefix + ["cookbooks", "lib", "1.0.0"], request)
426
+ .and_return(cookbook_json("lib", "1.0.0"))
427
+
428
+ result, cache = endpoint.depsolve(request, ["app"], desired, {})
429
+ expect(result["app"]).to eq(["1.0.0"])
430
+ expect(result["framework"]).to eq(["1.0.0"])
431
+ expect(result["lib"]).to eq(["2.0.0"])
432
+ expect(cache.keys).to contain_exactly("app", "framework", "lib")
433
+ end
434
+ end
435
+ end
436
+ end
@@ -1,4 +1,4 @@
1
- # Copyright: Copyright (c) 2012 Opscode, Inc.
1
+ # Copyright: Copyright (c) 2012-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
2
2
  # License: Apache License, Version 2.0
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chef-zero
3
3
  version: !ruby/object:Gem::Version
4
- version: 15.1.0
4
+ version: 15.1.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chef Software, Inc.
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
10
+ date: 2026-05-28 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: mixlib-log
@@ -53,16 +53,22 @@ dependencies:
53
53
  name: uuidtools
54
54
  requirement: !ruby/object:Gem::Requirement
55
55
  requirements:
56
- - - "~>"
56
+ - - ">="
57
57
  - !ruby/object:Gem::Version
58
58
  version: '2.1'
59
+ - - "<"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.0'
59
62
  type: :runtime
60
63
  prerelease: false
61
64
  version_requirements: !ruby/object:Gem::Requirement
62
65
  requirements:
63
- - - "~>"
66
+ - - ">="
64
67
  - !ruby/object:Gem::Version
65
68
  version: '2.1'
69
+ - - "<"
70
+ - !ruby/object:Gem::Version
71
+ version: '4.0'
66
72
  - !ruby/object:Gem::Dependency
67
73
  name: ffi-yajl
68
74
  requirement: !ruby/object:Gem::Requirement
@@ -89,54 +95,40 @@ dependencies:
89
95
  requirements:
90
96
  - - "~>"
91
97
  - !ruby/object:Gem::Version
92
- version: '3.1'
98
+ version: '3.2'
93
99
  - - ">="
94
100
  - !ruby/object:Gem::Version
95
- version: 3.1.16
101
+ version: 3.2.6
96
102
  type: :runtime
97
103
  prerelease: false
98
104
  version_requirements: !ruby/object:Gem::Requirement
99
105
  requirements:
100
106
  - - "~>"
101
107
  - !ruby/object:Gem::Version
102
- version: '3.1'
108
+ version: '3.2'
103
109
  - - ">="
104
110
  - !ruby/object:Gem::Version
105
- version: 3.1.16
111
+ version: 3.2.6
106
112
  - !ruby/object:Gem::Dependency
107
113
  name: rackup
108
114
  requirement: !ruby/object:Gem::Requirement
109
115
  requirements:
110
116
  - - "~>"
111
117
  - !ruby/object:Gem::Version
112
- version: '2.2'
118
+ version: '2.3'
113
119
  - - ">="
114
120
  - !ruby/object:Gem::Version
115
- version: 2.2.1
121
+ version: 2.3.1
116
122
  type: :runtime
117
123
  prerelease: false
118
124
  version_requirements: !ruby/object:Gem::Requirement
119
125
  requirements:
120
126
  - - "~>"
121
127
  - !ruby/object:Gem::Version
122
- version: '2.2'
128
+ version: '2.3'
123
129
  - - ">="
124
130
  - !ruby/object:Gem::Version
125
- version: 2.2.1
126
- - !ruby/object:Gem::Dependency
127
- name: unf_ext
128
- requirement: !ruby/object:Gem::Requirement
129
- requirements:
130
- - - "~>"
131
- - !ruby/object:Gem::Version
132
- version: 0.0.8
133
- type: :runtime
134
- prerelease: false
135
- version_requirements: !ruby/object:Gem::Requirement
136
- requirements:
137
- - - "~>"
138
- - !ruby/object:Gem::Version
139
- version: 0.0.8
131
+ version: 2.3.1
140
132
  - !ruby/object:Gem::Dependency
141
133
  name: webrick
142
134
  requirement: !ruby/object:Gem::Requirement
@@ -272,6 +264,7 @@ files:
272
264
  - lib/chef_zero/solr/solr_doc.rb
273
265
  - lib/chef_zero/solr/solr_parser.rb
274
266
  - lib/chef_zero/version.rb
267
+ - spec/endpoints/environment_cookbook_versions_endpoint_spec.rb
275
268
  - spec/run_oc_pedant.rb
276
269
  - spec/search_spec.rb
277
270
  - spec/server_spec.rb
@@ -297,7 +290,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
297
290
  - !ruby/object:Gem::Version
298
291
  version: '0'
299
292
  requirements: []
300
- rubygems_version: 3.7.2
293
+ rubygems_version: 3.6.2
301
294
  specification_version: 4
302
295
  summary: Self-contained, easy-setup, fast-start in-memory Chef server for testing
303
296
  and solo setup purposes