chef-dk 0.3.5 → 0.4.0

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.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +4 -4
  3. data/README.md +4 -4
  4. data/lib/chef-dk/builtin_commands.rb +4 -0
  5. data/lib/chef-dk/chef_runner.rb +7 -1
  6. data/lib/chef-dk/command/exec.rb +9 -0
  7. data/lib/chef-dk/command/export.rb +132 -0
  8. data/lib/chef-dk/command/generator_commands.rb +1 -1
  9. data/lib/chef-dk/command/generator_commands/app.rb +8 -0
  10. data/lib/chef-dk/command/generator_commands/base.rb +46 -4
  11. data/lib/chef-dk/command/generator_commands/cookbook.rb +8 -0
  12. data/lib/chef-dk/command/generator_commands/cookbook_code_file.rb +1 -0
  13. data/lib/chef-dk/command/push.rb +3 -6
  14. data/lib/chef-dk/command/shell_init.rb +28 -5
  15. data/lib/chef-dk/command/update.rb +106 -0
  16. data/lib/chef-dk/command/verify.rb +72 -0
  17. data/lib/chef-dk/component_test.rb +12 -1
  18. data/lib/chef-dk/configurable.rb +52 -0
  19. data/lib/chef-dk/cookbook_metadata.rb +10 -1
  20. data/lib/chef-dk/cookbook_profiler/git.rb +1 -1
  21. data/lib/chef-dk/exceptions.rb +17 -2
  22. data/lib/chef-dk/helpers.rb +2 -2
  23. data/lib/chef-dk/policyfile/community_cookbook_source.rb +1 -1
  24. data/lib/chef-dk/policyfile/dsl.rb +7 -0
  25. data/lib/chef-dk/policyfile/uploader.rb +25 -4
  26. data/lib/chef-dk/policyfile_compiler.rb +21 -1
  27. data/lib/chef-dk/policyfile_lock.rb +5 -0
  28. data/lib/chef-dk/policyfile_services/export_repo.rb +194 -0
  29. data/lib/chef-dk/policyfile_services/install.rb +8 -2
  30. data/lib/chef-dk/policyfile_services/push.rb +4 -1
  31. data/lib/chef-dk/service_exceptions.rb +6 -0
  32. data/lib/chef-dk/skeletons/code_generator/files/default/Berksfile +1 -1
  33. data/lib/chef-dk/skeletons/code_generator/files/default/repo/README.md +1 -1
  34. data/lib/chef-dk/skeletons/code_generator/files/default/repo/cookbooks/README.md +1 -1
  35. data/lib/chef-dk/skeletons/code_generator/files/default/repo/environments/README.md +2 -2
  36. data/lib/chef-dk/skeletons/code_generator/files/default/serverspec_spec_helper.rb +3 -0
  37. data/lib/chef-dk/skeletons/code_generator/files/default/spec_helper.rb +1 -7
  38. data/lib/chef-dk/skeletons/code_generator/metadata.rb +1 -1
  39. data/lib/chef-dk/skeletons/code_generator/recipes/app.rb +31 -1
  40. data/lib/chef-dk/skeletons/code_generator/recipes/cookbook.rb +32 -2
  41. data/lib/chef-dk/skeletons/code_generator/recipes/recipe.rb +18 -0
  42. data/lib/chef-dk/skeletons/code_generator/templates/default/recipe.rb.erb +5 -0
  43. data/lib/chef-dk/skeletons/code_generator/templates/default/recipe_spec.rb.erb +23 -0
  44. data/lib/chef-dk/skeletons/code_generator/templates/default/serverspec_default_spec.rb.erb +12 -0
  45. data/lib/chef-dk/version.rb +1 -1
  46. data/lib/kitchen/provisioner/policyfile_zero.rb +149 -0
  47. data/spec/shared/a_file_generator.rb +1 -0
  48. data/spec/shared/command_with_ui_object.rb +11 -0
  49. data/spec/shared/custom_generator_cookbook.rb +117 -0
  50. data/spec/unit/chef_runner_spec.rb +26 -0
  51. data/spec/unit/command/exec_spec.rb +46 -5
  52. data/spec/unit/command/export_spec.rb +176 -0
  53. data/spec/unit/command/generator_commands/app_spec.rb +38 -0
  54. data/spec/unit/command/generator_commands/cookbook_spec.rb +37 -28
  55. data/spec/unit/command/generator_commands/recipe_spec.rb +4 -2
  56. data/spec/unit/command/install_spec.rb +3 -6
  57. data/spec/unit/command/push_spec.rb +3 -6
  58. data/spec/unit/command/shell_init_spec.rb +77 -49
  59. data/spec/unit/command/update_spec.rb +155 -0
  60. data/spec/unit/command/verify_spec.rb +22 -7
  61. data/spec/unit/cookbook_metadata_spec.rb +44 -8
  62. data/spec/unit/cookbook_profiler/git_spec.rb +12 -0
  63. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/Berksfile +1 -1
  64. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/Berksfile +1 -1
  65. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/Berksfile +1 -1
  66. data/spec/unit/fixtures/cookbooks_api/small_universe.json +667 -667
  67. data/spec/unit/fixtures/cookbooks_api/universe.json +1 -1
  68. data/spec/unit/fixtures/cookbooks_api/update_fixtures.rb +1 -1
  69. data/spec/unit/fixtures/example_cookbook/Berksfile +1 -1
  70. data/spec/unit/fixtures/example_cookbook_metadata_json_only/.gitignore +17 -0
  71. data/spec/unit/fixtures/example_cookbook_metadata_json_only/.kitchen.yml +16 -0
  72. data/spec/unit/fixtures/example_cookbook_metadata_json_only/Berksfile +3 -0
  73. data/spec/unit/fixtures/example_cookbook_metadata_json_only/README.md +4 -0
  74. data/spec/unit/fixtures/example_cookbook_metadata_json_only/chefignore +96 -0
  75. data/spec/unit/fixtures/example_cookbook_metadata_json_only/metadata.json +5 -0
  76. data/spec/unit/fixtures/example_cookbook_metadata_json_only/recipes/default.rb +8 -0
  77. data/spec/unit/fixtures/example_cookbook_no_metadata/.gitignore +17 -0
  78. data/spec/unit/fixtures/example_cookbook_no_metadata/.kitchen.yml +16 -0
  79. data/spec/unit/fixtures/example_cookbook_no_metadata/Berksfile +3 -0
  80. data/spec/unit/fixtures/example_cookbook_no_metadata/README.md +4 -0
  81. data/spec/unit/fixtures/example_cookbook_no_metadata/chefignore +96 -0
  82. data/spec/unit/fixtures/example_cookbook_no_metadata/recipes/default.rb +8 -0
  83. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/Berksfile +1 -1
  84. data/spec/unit/policyfile/community_cookbook_source_spec.rb +2 -2
  85. data/spec/unit/policyfile/cookbook_location_specification_spec.rb +3 -3
  86. data/spec/unit/policyfile/uploader_spec.rb +61 -25
  87. data/spec/unit/policyfile_demands_spec.rb +47 -0
  88. data/spec/unit/policyfile_evaluation_spec.rb +1 -1
  89. data/spec/unit/policyfile_lock_build_spec.rb +60 -3
  90. data/spec/unit/policyfile_services/export_repo_spec.rb +321 -0
  91. data/spec/unit/policyfile_services/install_spec.rb +20 -1
  92. data/spec/unit/policyfile_services/push_spec.rb +36 -9
  93. metadata +53 -38
  94. data/lib/chef-dk/skeletons/code_generator/files/default/converge_spec.rb +0 -9
  95. data/lib/chef-dk/skeletons/code_generator/templates/default/default_recipe.rb.erb +0 -5
@@ -148,6 +148,29 @@ describe ChefDK::PolicyfileCompiler, "when expressing the Policyfile graph deman
148
148
  expect(policyfile.normalized_run_list).to eq(["recipe[local-cookbook::jazz_hands]"])
149
149
  end
150
150
 
151
+ describe "in an alternate run list" do
152
+
153
+ it "normalizes a bare cookbook name" do
154
+ policyfile.named_run_list(:foo, "local-cookbook")
155
+ expect(policyfile.normalized_named_run_lists[:foo]).to eq(["recipe[local-cookbook::default]"])
156
+ end
157
+
158
+ it "normalizes a bare cookbook::recipe item" do
159
+ policyfile.named_run_list(:foo, "local-cookbook::server")
160
+ expect(policyfile.normalized_named_run_lists[:foo]).to eq(["recipe[local-cookbook::server]"])
161
+ end
162
+
163
+ it "normalizes a recipe[] item with implicit default" do
164
+ policyfile.named_run_list(:foo, "recipe[local-cookbook]")
165
+ expect(policyfile.normalized_named_run_lists[:foo]).to eq(["recipe[local-cookbook::default]"])
166
+ end
167
+
168
+ it "does not modify a fully qualified recipe" do
169
+ policyfile.named_run_list(:foo, "recipe[local-cookbook::jazz_hands]")
170
+ expect(policyfile.normalized_named_run_lists[:foo]).to eq(["recipe[local-cookbook::jazz_hands]"])
171
+ end
172
+
173
+ end
151
174
  end
152
175
 
153
176
  before do
@@ -628,6 +651,30 @@ describe ChefDK::PolicyfileCompiler, "when expressing the Policyfile graph deman
628
651
  end
629
652
  end
630
653
 
654
+ context "Given a run_list and named run_lists" do
655
+
656
+ before do
657
+ policyfile.dsl.named_run_list(:foo, 'local-cookbook', 'nginx')
658
+ policyfile.dsl.named_run_list(:bar, 'remote-cb', 'nginx')
659
+ policyfile.dsl.run_list('private-cookbook', 'nginx')
660
+ end
661
+
662
+ it "demands a solution that satisfies all of the run lists, with no duplicates" do
663
+ expect(policyfile.graph_demands).to include(["private-cookbook", ">= 0.0.0"])
664
+ expect(policyfile.graph_demands).to include(["nginx", ">= 0.0.0"])
665
+ expect(policyfile.graph_demands).to include(["remote-cb", ">= 0.0.0"])
666
+ expect(policyfile.graph_demands).to include(["local-cookbook", ">= 0.0.0"])
667
+
668
+ # ensure there are no duplicates:
669
+ expected_demands = [["private-cookbook", ">= 0.0.0"],
670
+ ["nginx", ">= 0.0.0"],
671
+ ["local-cookbook", ">= 0.0.0"],
672
+ ["remote-cb", ">= 0.0.0"]]
673
+ expect(policyfile.graph_demands).to eq(expected_demands)
674
+ end
675
+
676
+ end
677
+
631
678
  context "Given a run_list with roles" do
632
679
  it "expands the roles from the given role source" do
633
680
  skip
@@ -185,7 +185,7 @@ E
185
185
 
186
186
  it "has a default source" do
187
187
  expect(policyfile.errors).to eq([])
188
- expected = ChefDK::Policyfile::CommunityCookbookSource.new("https://supermarket.getchef.com")
188
+ expected = ChefDK::Policyfile::CommunityCookbookSource.new("https://supermarket.chef.io")
189
189
  expect(policyfile.default_source).to eq(expected)
190
190
  end
191
191
 
@@ -341,7 +341,7 @@ describe ChefDK::PolicyfileLock, "building a lockfile" do
341
341
 
342
342
  # Optional attribute that humans can use to understand where a cookbook
343
343
  # came from.
344
- cb.origin = "https://community.getchef.com/api/cookbooks/foo/1.0.0"
344
+ cb.origin = "https://community.chef.io/api/cookbooks/foo/1.0.0"
345
345
  end
346
346
 
347
347
  p.local_cookbook("bar") do |cb|
@@ -375,7 +375,7 @@ describe ChefDK::PolicyfileLock, "building a lockfile" do
375
375
  "version" => "1.0.0",
376
376
  "identifier" => cookbook_foo_cksum,
377
377
  "dotted_decimal_identifier" => cookbook_foo_cksum_dotted,
378
- "origin" => "https://community.getchef.com/api/cookbooks/foo/1.0.0",
378
+ "origin" => "https://community.chef.io/api/cookbooks/foo/1.0.0",
379
379
  "cache_key" => "foo-1.0.0",
380
380
  "source_options" => nil
381
381
  },
@@ -492,6 +492,56 @@ describe ChefDK::PolicyfileLock, "building a lockfile" do
492
492
  end
493
493
 
494
494
  end
495
+
496
+ context "with named run_lists specified" do
497
+
498
+ let(:policyfile_lock) do
499
+ ChefDK::PolicyfileLock.build(storage_config) do |p|
500
+
501
+ p.name = "minimal_policyfile"
502
+
503
+ p.run_list = [ "recipe[foo]" ]
504
+
505
+ p.named_run_lists = { "rl2" => [ "recipe[foo::bar]" ] }
506
+
507
+ p.cached_cookbook("foo") do |cb|
508
+ cb.cache_key = "foo-1.0.0"
509
+ end
510
+
511
+ end
512
+ end
513
+
514
+ let(:compiled_policyfile) do
515
+ {
516
+
517
+ "name" => "minimal_policyfile",
518
+
519
+ "run_list" => ["recipe[foo]"],
520
+
521
+ "named_run_lists" => { "rl2" => [ "recipe[foo::bar]" ] },
522
+
523
+ "cookbook_locks" => {
524
+
525
+ "foo" => {
526
+ "version" => "1.0.0",
527
+ "identifier" => cookbook_foo_cksum,
528
+ "dotted_decimal_identifier" => cookbook_foo_cksum_dotted,
529
+ "cache_key" => "foo-1.0.0",
530
+ "origin" => nil,
531
+ "source_options" => nil
532
+ },
533
+ },
534
+
535
+ "solution_dependencies" => { "Policyfile" => [], "dependencies" => {} }
536
+ }
537
+ end
538
+
539
+ it "includes the named run lists in the compiled policyfile" do
540
+ expect(policyfile_lock.to_lock).to eq(compiled_policyfile)
541
+ end
542
+
543
+ end
544
+
495
545
  describe "building a policyfile lock from a policyfile compiler" do
496
546
 
497
547
  include_context "setup git cookbooks"
@@ -500,7 +550,7 @@ describe ChefDK::PolicyfileLock, "building a lockfile" do
500
550
  tempdir
501
551
  end
502
552
 
503
- let(:cached_cookbook_uri) { "https://supermarket.getchef.com/api/v1/cookbooks/foo/versions/1.0.0/download" }
553
+ let(:cached_cookbook_uri) { "https://supermarket.chef.io/api/v1/cookbooks/foo/versions/1.0.0/download" }
504
554
 
505
555
  let(:cached_location_spec) do
506
556
  double( "ChefDK::Policyfile::CookbookLocationSpecification",
@@ -530,6 +580,7 @@ describe ChefDK::PolicyfileLock, "building a lockfile" do
530
580
  double( "ChefDK::PolicyfileCompiler",
531
581
  name: "my-policyfile",
532
582
  normalized_run_list: %w[recipe[foo::default] recipe[bar::default]],
583
+ normalized_named_run_lists: { "rl2" => %w[recipe[bar::default]] },
533
584
  all_cookbook_location_specs: {"foo" => cached_location_spec, "bar" => local_location_spec},
534
585
  solution_dependencies: policyfile_solution_dependencies )
535
586
  end
@@ -545,6 +596,8 @@ describe ChefDK::PolicyfileLock, "building a lockfile" do
545
596
 
546
597
  "run_list" => ["recipe[foo::default]", "recipe[bar::default]"],
547
598
 
599
+ "named_run_lists" => { "rl2" => ["recipe[bar::default]"] },
600
+
548
601
  "cookbook_locks" => {
549
602
 
550
603
  "foo" => {
@@ -595,6 +648,10 @@ describe ChefDK::PolicyfileLock, "building a lockfile" do
595
648
  expect(cb_lock.source).to eq(local_location_spec.relative_path)
596
649
  end
597
650
 
651
+ it "sets named run lists on the policyfile lock" do
652
+ expect(policyfile_lock.named_run_lists).to eq("rl2" => %w[recipe[bar::default]])
653
+ end
654
+
598
655
  it "generates a lockfile data structure" do
599
656
  expect(policyfile_lock.to_lock).to eq(compiled_policyfile)
600
657
  end
@@ -0,0 +1,321 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2014 Chef Software Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'spec_helper'
19
+ require 'chef-dk/policyfile_services/export_repo'
20
+
21
+ describe ChefDK::PolicyfileServices::ExportRepo do
22
+
23
+ let(:working_dir) do
24
+ path = File.join(tempdir, "policyfile_services_test_working_dir")
25
+ Dir.mkdir(path)
26
+ path
27
+ end
28
+
29
+ let(:export_dir) { File.join(tempdir, "export_repo_export_dir") }
30
+
31
+ let(:policyfile_rb_explicit_name) { nil }
32
+
33
+ let(:policyfile_rb_name) { policyfile_rb_explicit_name || "Policyfile.rb" }
34
+
35
+ let(:expanded_policyfile_path) { File.join(working_dir, policyfile_rb_name) }
36
+
37
+ let(:policyfile_lock_name) { "Policyfile.lock.json" }
38
+
39
+ let(:policyfile_lock_path) { File.join(working_dir, policyfile_lock_name) }
40
+
41
+ let(:force_export) { false }
42
+
43
+ subject(:export_service) do
44
+ described_class.new(policyfile: policyfile_rb_explicit_name,
45
+ root_dir: working_dir,
46
+ export_dir: export_dir,
47
+ force: force_export)
48
+ end
49
+
50
+ it "uses Policyfile.rb as the default Policyfile name" do
51
+ expect(export_service.policyfile_filename).to eq(expanded_policyfile_path)
52
+ end
53
+
54
+ context "when given an explicit Policyfile name" do
55
+
56
+ let(:policyfile_rb_explicit_name) { "MyPolicy.rb" }
57
+
58
+ it "uses the given Policyfile name" do
59
+ expect(export_service.policyfile_filename).to eq(expanded_policyfile_path)
60
+ end
61
+
62
+ end
63
+
64
+ it "has a destination directory for the export" do
65
+ expect(export_service.export_dir).to eq(export_dir)
66
+ end
67
+
68
+ context "when the policyfile lock is missing" do
69
+
70
+ it "raises an error that suggests you run `chef install'" do
71
+ expect { export_service.run }.to raise_error(ChefDK::LockfileNotFound)
72
+ end
73
+
74
+ end
75
+
76
+ context "when a lockfile is present" do
77
+
78
+ before do
79
+ File.open(policyfile_lock_path, "w+") { |f| f.print(lockfile_content) }
80
+ end
81
+
82
+ context "and the lockfile has invalid JSON" do
83
+
84
+ let(:lockfile_content) { ":::" }
85
+
86
+ it "errors out" do
87
+ expect { export_service.run }.to raise_error(ChefDK::PolicyfileExportRepoError, /Error reading lockfile/)
88
+ end
89
+
90
+ end
91
+
92
+ context "and the lockfile is semantically invalid" do
93
+
94
+ let(:lockfile_content) { '{ }' }
95
+
96
+ it "errors out" do
97
+ expect { export_service.run }.to raise_error(ChefDK::PolicyfileExportRepoError, /Invalid lockfile data/)
98
+ end
99
+
100
+ end
101
+
102
+ context "and the lockfile is valid" do
103
+
104
+ let(:local_cookbook_path) { File.join(fixtures_path, "local_path_cookbooks/local-cookbook") }
105
+
106
+ let(:lockfile_content) do
107
+ <<-E
108
+ {
109
+ "name": "install-example",
110
+ "run_list": [
111
+ "recipe[local-cookbook::default]"
112
+ ],
113
+ "cookbook_locks": {
114
+ "local-cookbook": {
115
+ "version": "2.3.4",
116
+ "identifier": "fab501cfaf747901bd82c1bc706beae7dc3a350c",
117
+ "dotted_decimal_identifier": "70567763561641081.489844270461035.258281553147148",
118
+ "source": "#{local_cookbook_path}",
119
+ "cache_key": null,
120
+ "scm_info": null,
121
+ "source_options": {
122
+ "path": "#{local_cookbook_path}"
123
+ }
124
+ }
125
+ },
126
+ "solution_dependencies": {
127
+ "Policyfile": [
128
+ [
129
+ "local-cookbook",
130
+ ">= 0.0.0"
131
+ ]
132
+ ],
133
+ "dependencies": {
134
+ "local-cookbook (2.3.4)": [
135
+
136
+ ]
137
+ }
138
+ }
139
+ }
140
+ E
141
+ end
142
+
143
+ it "reads the lockfile data" do
144
+ lock = export_service.policyfile_lock
145
+ expect(lock).to be_an_instance_of(ChefDK::PolicyfileLock)
146
+ expect(lock.name).to eq("install-example")
147
+ expect(lock.cookbook_locks.size).to eq(1)
148
+ expect(lock.cookbook_locks).to have_key("local-cookbook")
149
+ end
150
+
151
+ it "delegates #policy_name to the lockfile" do
152
+ expect(export_service.policy_name).to eq("install-example")
153
+ end
154
+
155
+ describe "writing updates to the policyfile lock" do
156
+
157
+ let(:updated_lockfile_io) { StringIO.new }
158
+
159
+ it "validates the lockfile and writes updates to disk" do
160
+ allow(File).to receive(:open).and_call_original
161
+ expect(File).to receive(:open).with(policyfile_lock_path, "wb+").and_yield(updated_lockfile_io)
162
+
163
+ export_service.run
164
+ end
165
+
166
+ end
167
+
168
+ context "copying the cookbooks to the export dir" do
169
+
170
+ context "when the export dir is empty" do
171
+ before do
172
+ allow(export_service.policyfile_lock).to receive(:validate_cookbooks!).and_return(true)
173
+ export_service.run
174
+ end
175
+
176
+ let(:cookbook_files) do
177
+ base_pathname = Pathname.new(local_cookbook_path)
178
+ Dir.glob("#{local_cookbook_path}/**/*").map do |full_path|
179
+ Pathname.new(full_path).relative_path_from(base_pathname)
180
+ end
181
+ end
182
+
183
+ let(:expected_files_relative) do
184
+ metadata_rb = Pathname.new("metadata.rb")
185
+ expected = cookbook_files.delete_if { |p| p == metadata_rb }
186
+ expected << Pathname.new("metadata.json")
187
+ end
188
+
189
+ let(:cookbook_with_version) { "local-cookbook-70567763561641081.489844270461035.258281553147148" }
190
+
191
+ let(:exported_cookbook_root) { Pathname.new(File.join(export_dir, "cookbooks", cookbook_with_version)) }
192
+
193
+ let(:expected_files) do
194
+ expected_files_relative.map do |file_rel_path|
195
+ exported_cookbook_root + file_rel_path
196
+ end
197
+ end
198
+
199
+ it "copies cookbooks to the target dir in versioned_cookbooks format" do
200
+ expected_files.each do |expected_file|
201
+ expect(expected_file).to exist
202
+ end
203
+ end
204
+
205
+ # This behavior does two things:
206
+ # * ensures that Chef Zero uses our hacked version number
207
+ # * works around external dependencies (e.g., using `git` in backticks)
208
+ # in metadata.rb issue
209
+ it "writes metadata.json in the exported cookbook, removing metadata.rb" do
210
+ metadata_json_path = File.join(exported_cookbook_root, "metadata.json")
211
+ metadata_json = FFI_Yajl::Parser.parse(IO.read(metadata_json_path))
212
+ expect(metadata_json["version"]).to eq("70567763561641081.489844270461035.258281553147148")
213
+ end
214
+
215
+ it "copies the policyfile lock in data item format to data_bags/policyfiles" do
216
+ data_bag_item_path = File.join(export_dir, "data_bags", "policyfiles", "install-example-local.json")
217
+ data_item_json = FFI_Yajl::Parser.parse(IO.read(data_bag_item_path))
218
+ expect(data_item_json["id"]).to eq("install-example-local")
219
+ end
220
+
221
+ end
222
+
223
+ context "When an error occurs creating the export" do
224
+
225
+ before do
226
+ allow(export_service.policyfile_lock).to receive(:validate_cookbooks!).and_return(true)
227
+ expect(export_service).to receive(:create_repo_structure).
228
+ and_raise(Errno::EACCES.new("Permission denied @ rb_sysopen - /etc/foobarbaz.txt"))
229
+ end
230
+
231
+ it "wraps the error in a custom error class" do
232
+ message = "Failed to export policy (in #{expanded_policyfile_path}) to #{export_dir}"
233
+ expect { export_service.run }.to raise_error(ChefDK::PolicyfileExportRepoError, message)
234
+ end
235
+
236
+ end
237
+
238
+ context "When the export dir has non-conflicting content" do
239
+
240
+ let(:file_in_export_dir) { File.join(export_dir, "some_random_cruft") }
241
+
242
+ let(:extra_data_bag_dir) { File.join(export_dir, "data_bags", "extraneous") }
243
+
244
+ let(:extra_data_bag_item) { File.join(extra_data_bag_dir, "an_item.json") }
245
+
246
+ before do
247
+ FileUtils.mkdir_p(export_dir)
248
+ File.open(file_in_export_dir, "wb+") { |f| f.print "some random cruft" }
249
+ FileUtils.mkdir_p(extra_data_bag_dir)
250
+ File.open(extra_data_bag_item, "wb+") { |f| f.print "some random cruft" }
251
+ end
252
+
253
+ it "ignores the non-conflicting content and exports" do
254
+ export_service.run
255
+
256
+ expect(File).to exist(file_in_export_dir)
257
+ expect(File).to exist(extra_data_bag_item)
258
+
259
+ expect(File).to be_directory(File.join(export_dir, "cookbooks"))
260
+ expect(File).to be_directory(File.join(export_dir, "data_bags"))
261
+ end
262
+ end
263
+
264
+ context "When the export dir has conflicting content" do
265
+
266
+ let(:non_conflicting_file_in_export_dir) { File.join(export_dir, "some_random_cruft") }
267
+
268
+ let(:cookbooks_dir) { File.join(export_dir, "cookbooks") }
269
+
270
+ let(:file_in_cookbooks_dir) { File.join(cookbooks_dir, "some_random_cruft") }
271
+
272
+ let(:policyfiles_data_bag_dir) { File.join(export_dir, "data_bags", "policyfiles") }
273
+
274
+ let(:extra_policyfile_data_item) { File.join(policyfiles_data_bag_dir, "leftover-policy.json") }
275
+
276
+ before do
277
+ FileUtils.mkdir_p(export_dir)
278
+ FileUtils.mkdir_p(cookbooks_dir)
279
+ FileUtils.mkdir_p(policyfiles_data_bag_dir)
280
+ File.open(non_conflicting_file_in_export_dir, "wb+") { |f| f.print "some random cruft" }
281
+ File.open(file_in_cookbooks_dir, "wb+") { |f| f.print "some random cruft" }
282
+ File.open(extra_policyfile_data_item, "wb+") { |f| f.print "some random cruft" }
283
+ end
284
+
285
+ it "raises a PolicyfileExportRepoError" do
286
+ message = "Export dir (#{export_dir}) not clean. Refusing to export. (Conflicting files: #{file_in_cookbooks_dir}, #{extra_policyfile_data_item})"
287
+ expect { export_service.run }.to raise_error(ChefDK::ExportDirNotEmpty, message)
288
+ expect(File).to exist(non_conflicting_file_in_export_dir)
289
+ expect(File).to exist(file_in_cookbooks_dir)
290
+ expect(File).to exist(extra_policyfile_data_item)
291
+ end
292
+
293
+ context "and the force option is set" do
294
+
295
+ let(:force_export) { true }
296
+
297
+ it "clears the export dir and exports" do
298
+ export_service.run
299
+
300
+ expect(File).to_not exist(file_in_cookbooks_dir)
301
+ expect(File).to_not exist(extra_policyfile_data_item)
302
+
303
+ expect(File).to exist(non_conflicting_file_in_export_dir)
304
+
305
+ expect(File).to be_directory(File.join(export_dir, "cookbooks"))
306
+ expect(File).to be_directory(File.join(export_dir, "data_bags"))
307
+ end
308
+
309
+ end
310
+
311
+ end
312
+
313
+ end
314
+ end
315
+
316
+ end
317
+
318
+
319
+
320
+ end
321
+