knife-ec-backup 3.0.5 → 3.0.9

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.
@@ -1,6 +1,7 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
2
2
  require 'chef/knife/ec_key_base'
3
3
  require 'chef/automate'
4
+ require 'sequel'
4
5
 
5
6
  class KeyBaseTester < Chef::Knife
6
7
  include Chef::Knife::EcKeyBase
@@ -38,4 +39,54 @@ describe Chef::Knife::EcKeyBase do
38
39
  expect(knife.config[:sql_password]).to eq("secrete")
39
40
  end
40
41
  end
42
+
43
+ # Regression coverage for the external-PostgreSQL bug: the SQL host/port must
44
+ # be sourced from chef-server-running.json (like sql_user/sql_password/sql_db),
45
+ # while an explicit --sql-host / --sql-port supplied on the CLI must still win.
46
+ describe "#load_config_from_file! PostgreSQL host/port autoconfiguration" do
47
+ let(:external_pg_running_config) {
48
+ '{"private_chef": { "opscode-erchef": {}, "postgresql": { "vip": "db.external.example.com", "port": 6432, "sql_user": "jiminy", "sql_password": "secret" } } }'
49
+ }
50
+
51
+ before(:each) do
52
+ allow(Chef::Automate).to receive(:is_installed?).and_return(false)
53
+ allow(File).to receive(:exist?).and_call_original
54
+ allow(File).to receive(:exist?).with("/etc/opscode/chef-server-running.json").and_return(true)
55
+ allow(File).to receive(:read).and_call_original
56
+ allow(File).to receive(:read).with("/etc/opscode/chef-server-running.json").and_return(external_pg_running_config)
57
+ end
58
+
59
+ it "adopts the PostgreSQL host and port from chef-server-running.json when no CLI flag is given" do
60
+ knife.load_config_from_file!
61
+ expect(knife.config[:sql_host]).to eq("db.external.example.com")
62
+ expect(knife.config[:sql_port]).to eq(6432)
63
+ end
64
+
65
+ it "lets an explicit --sql-host / --sql-port win over the autoconfigured value" do
66
+ knife.config[:sql_host] = "cli-host.example.com"
67
+ knife.config[:sql_port] = 2345
68
+ knife.load_config_from_file!
69
+ expect(knife.config[:sql_host]).to eq("cli-host.example.com")
70
+ expect(knife.config[:sql_port]).to eq(2345)
71
+ end
72
+ end
73
+
74
+ # Regression coverage for #181: the configured database name must be written
75
+ # into the PostgreSQL connection URI. It was previously resolved but never
76
+ # applied, so PostgreSQL silently fell back to a database named after the user.
77
+ describe "#db connection URI" do
78
+ before(:each) do
79
+ # Capture the connection string instead of opening a real connection.
80
+ allow(Sequel).to receive(:connect) { |uri, *| uri }
81
+ end
82
+
83
+ it "includes the configured database name in the connection URI" do
84
+ knife.config[:sql_db] = "custom_db"
85
+ expect(knife.db).to include("/custom_db")
86
+ end
87
+
88
+ it "falls back to localhost:5432 when host and port are unset" do
89
+ expect(knife.db).to start_with("postgres://localhost:5432")
90
+ end
91
+ end
41
92
  end
@@ -184,4 +184,194 @@ describe Chef::Knife::EcRestore do
184
184
  end
185
185
  end
186
186
  end
187
+
188
+ describe "#restore_cookbook_frozen_status" do
189
+ include FakeFS::SpecHelpers
190
+
191
+ let(:org_name) { "test_org" }
192
+ let(:chef_fs_config) { double("Chef::ChefFS::Config") }
193
+ let(:cookbooks_path) { "/organizations/#{org_name}/cookbooks" }
194
+
195
+ before(:each) do
196
+ allow(@knife).to receive(:dest_dir).and_return("")
197
+ allow(@knife).to receive(:ui).and_return(double("ui", :msg => nil, :warn => nil))
198
+ end
199
+
200
+ context "when cookbooks directory does not exist" do
201
+ it "returns early without processing" do
202
+ expect(@knife).not_to receive(:freeze_cookbook)
203
+ @knife.restore_cookbook_frozen_status(org_name, chef_fs_config)
204
+ end
205
+ end
206
+
207
+ context "when cookbooks directory exists" do
208
+ before(:each) do
209
+ FileUtils.mkdir_p(cookbooks_path)
210
+ end
211
+
212
+ context "with no cookbook directories" do
213
+ it "does not process any cookbooks" do
214
+ expect(@knife).not_to receive(:freeze_cookbook)
215
+ @knife.restore_cookbook_frozen_status(org_name, chef_fs_config)
216
+ end
217
+ end
218
+
219
+ context "with cookbook directories" do
220
+ let(:cookbook_name) { "apache2" }
221
+ let(:version) { "1.2.3" }
222
+ let(:cookbook_dir) { "#{cookbook_name}-#{version}" }
223
+ let(:cookbook_path) { File.join(cookbooks_path, cookbook_dir) }
224
+ let(:status_file) { File.join(cookbook_path, "status.json") }
225
+
226
+ before(:each) do
227
+ FileUtils.mkdir_p(cookbook_path)
228
+ end
229
+
230
+ context "when status.json does not exist" do
231
+ it "skips the cookbook" do
232
+ expect(@knife).not_to receive(:freeze_cookbook)
233
+ @knife.restore_cookbook_frozen_status(org_name, chef_fs_config)
234
+ end
235
+ end
236
+
237
+ context "when status.json exists" do
238
+ context "and cookbook is frozen" do
239
+ before(:each) do
240
+ File.write(status_file, '{"frozen": true}')
241
+ end
242
+
243
+ it "calls freeze_cookbook with correct parameters" do
244
+ expect(@knife).to receive(:freeze_cookbook).with(cookbook_name, version, org_name)
245
+ @knife.restore_cookbook_frozen_status(org_name, chef_fs_config)
246
+ end
247
+ end
248
+
249
+ context "and cookbook is not frozen" do
250
+ before(:each) do
251
+ File.write(status_file, '{"frozen": false}')
252
+ end
253
+
254
+ it "does not call freeze_cookbook" do
255
+ expect(@knife).not_to receive(:freeze_cookbook)
256
+ @knife.restore_cookbook_frozen_status(org_name, chef_fs_config)
257
+ end
258
+ end
259
+
260
+ context "and status.json has invalid JSON" do
261
+ before(:each) do
262
+ File.write(status_file, 'invalid json')
263
+ end
264
+
265
+ it "warns about parse error and continues" do
266
+ ui = double("ui")
267
+ allow(@knife).to receive(:ui).and_return(ui)
268
+ expect(ui).to receive(:msg).with("Restoring cookbook frozen status")
269
+ expect(ui).to receive(:warn).with(/Failed to parse status\.json/)
270
+ expect(@knife).not_to receive(:freeze_cookbook)
271
+ @knife.restore_cookbook_frozen_status(org_name, chef_fs_config)
272
+ end
273
+ end
274
+
275
+ context "with multiple cookbooks" do
276
+ let(:frozen_cookbook) { "frozen-cookbook-2.0.0" }
277
+ let(:unfrozen_cookbook) { "unfrozen-cookbook-1.5.0" }
278
+
279
+ before(:each) do
280
+ # Create frozen cookbook
281
+ FileUtils.mkdir_p(File.join(cookbooks_path, frozen_cookbook))
282
+ File.write(File.join(cookbooks_path, frozen_cookbook, "status.json"), '{"frozen": true}')
283
+
284
+ # Create unfrozen cookbook
285
+ FileUtils.mkdir_p(File.join(cookbooks_path, unfrozen_cookbook))
286
+ File.write(File.join(cookbooks_path, unfrozen_cookbook, "status.json"), '{"frozen": false}')
287
+ end
288
+
289
+ it "only freezes the frozen cookbook" do
290
+ expect(@knife).to receive(:freeze_cookbook).with("frozen-cookbook", "2.0.0", org_name).once
291
+ expect(@knife).not_to receive(:freeze_cookbook).with("unfrozen-cookbook", "1.5.0", org_name)
292
+ @knife.restore_cookbook_frozen_status(org_name, chef_fs_config)
293
+ end
294
+ end
295
+
296
+ context "with cookbook name containing hyphens" do
297
+ let(:cookbook_name) { "multi-word-cookbook" }
298
+ let(:version) { "1.0.0" }
299
+
300
+ before(:each) do
301
+ cookbook_dir = "#{cookbook_name}-#{version}"
302
+ cookbook_path = File.join(cookbooks_path, cookbook_dir)
303
+ FileUtils.mkdir_p(cookbook_path)
304
+ File.write(File.join(cookbook_path, "status.json"), '{"frozen": true}')
305
+ end
306
+
307
+ it "correctly parses cookbook name with hyphens" do
308
+ expect(@knife).to receive(:freeze_cookbook).with(cookbook_name, version, org_name)
309
+ @knife.restore_cookbook_frozen_status(org_name, chef_fs_config)
310
+ end
311
+ end
312
+ end
313
+ end
314
+ end
315
+ end
316
+
317
+ describe "#freeze_cookbook" do
318
+ let(:cookbook_name) { "apache2" }
319
+ let(:version) { "1.2.3" }
320
+ let(:org_name) { "test_org" }
321
+ let(:manifest) { { "name" => cookbook_name, "version" => version, "frozen?" => false } }
322
+
323
+ before(:each) do
324
+ allow(@knife).to receive(:ui).and_return(double("ui", :msg => nil, :warn => nil))
325
+ end
326
+
327
+ context "when cookbook is not already frozen" do
328
+ before(:each) do
329
+ allow(@rest).to receive(:get).with("organizations/#{org_name}/cookbooks/#{cookbook_name}/#{version}").and_return(manifest)
330
+ end
331
+
332
+ it "freezes the cookbook successfully" do
333
+ frozen_manifest = manifest.dup.tap { |h| h["frozen?"] = true }
334
+ expect(@rest).to receive(:put).with("organizations/#{org_name}/cookbooks/#{cookbook_name}/#{version}?freeze=true", frozen_manifest)
335
+ @knife.freeze_cookbook(cookbook_name, version, org_name)
336
+ end
337
+ end
338
+
339
+ context "when cookbook is already frozen" do
340
+ let(:frozen_manifest) { { "name" => cookbook_name, "version" => version, "frozen?" => true } }
341
+
342
+ before(:each) do
343
+ allow(@rest).to receive(:get).with("organizations/#{org_name}/cookbooks/#{cookbook_name}/#{version}").and_return(frozen_manifest)
344
+ end
345
+
346
+ it "skips freezing and warns" do
347
+ ui = double("ui")
348
+ allow(@knife).to receive(:ui).and_return(ui)
349
+ expect(ui).to receive(:msg).with("Freezing cookbook #{cookbook_name} version #{version}")
350
+ expect(ui).to receive(:warn).with(/already frozen/)
351
+ expect(@rest).not_to receive(:put)
352
+ @knife.freeze_cookbook(cookbook_name, version, org_name)
353
+ end
354
+ end
355
+
356
+ context "when API call fails" do
357
+ before(:each) do
358
+ allow(@rest).to receive(:get).with("organizations/#{org_name}/cookbooks/#{cookbook_name}/#{version}").and_return(manifest)
359
+ allow(@rest).to receive(:put).and_raise(net_exception(500))
360
+ allow(@knife).to receive(:knife_ec_error_handler).and_return(double("error_handler", :add => nil))
361
+ end
362
+
363
+ it "handles the exception and adds to error handler" do
364
+ ui = double("ui")
365
+ error_handler = double("error_handler")
366
+ allow(@knife).to receive(:ui).and_return(ui)
367
+ allow(@knife).to receive(:knife_ec_error_handler).and_return(error_handler)
368
+
369
+ expect(ui).to receive(:msg).with("Freezing cookbook #{cookbook_name} version #{version}")
370
+ expect(ui).to receive(:warn).with(/Failed to freeze cookbook/)
371
+ expect(error_handler).to receive(:add)
372
+
373
+ @knife.freeze_cookbook(cookbook_name, version, org_name)
374
+ end
375
+ end
376
+ end
187
377
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife-ec-backup
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.5
4
+ version: 3.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Keiser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-08-19 00:00:00.000000000 Z
11
+ date: 2026-06-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
@@ -95,6 +95,7 @@ files:
95
95
  - lib/chef/knife/ec_backup.rb
96
96
  - lib/chef/knife/ec_base.rb
97
97
  - lib/chef/knife/ec_error_handler.rb
98
+ - lib/chef/knife/ec_import.rb
98
99
  - lib/chef/knife/ec_key_base.rb
99
100
  - lib/chef/knife/ec_key_export.rb
100
101
  - lib/chef/knife/ec_key_import.rb
@@ -106,6 +107,7 @@ files:
106
107
  - spec/chef/knife/ec_backup_spec.rb
107
108
  - spec/chef/knife/ec_base_spec.rb
108
109
  - spec/chef/knife/ec_error_handler_spec.rb
110
+ - spec/chef/knife/ec_import_spec.rb
109
111
  - spec/chef/knife/ec_key_base_spec.rb
110
112
  - spec/chef/knife/ec_key_export_spec.rb
111
113
  - spec/chef/knife/ec_key_import_spec.rb