chef-dk 0.2.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 (142) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +192 -0
  3. data/LICENSE +201 -0
  4. data/README.md +198 -0
  5. data/Rakefile +18 -0
  6. data/bin/chef +25 -0
  7. data/lib/chef-dk.rb +19 -0
  8. data/lib/chef-dk/builtin_commands.rb +31 -0
  9. data/lib/chef-dk/chef_runner.rb +83 -0
  10. data/lib/chef-dk/cli.rb +135 -0
  11. data/lib/chef-dk/command/base.rb +71 -0
  12. data/lib/chef-dk/command/exec.rb +33 -0
  13. data/lib/chef-dk/command/gem.rb +47 -0
  14. data/lib/chef-dk/command/generate.rb +97 -0
  15. data/lib/chef-dk/command/generator_commands.rb +417 -0
  16. data/lib/chef-dk/command/shell_init.rb +80 -0
  17. data/lib/chef-dk/command/verify.rb +226 -0
  18. data/lib/chef-dk/commands_map.rb +115 -0
  19. data/lib/chef-dk/component_test.rb +142 -0
  20. data/lib/chef-dk/cookbook_metadata.rb +36 -0
  21. data/lib/chef-dk/cookbook_omnifetch.rb +29 -0
  22. data/lib/chef-dk/cookbook_profiler/git.rb +95 -0
  23. data/lib/chef-dk/cookbook_profiler/identifiers.rb +79 -0
  24. data/lib/chef-dk/cookbook_profiler/null_scm.rb +32 -0
  25. data/lib/chef-dk/exceptions.rb +46 -0
  26. data/lib/chef-dk/generator.rb +70 -0
  27. data/lib/chef-dk/helpers.rb +95 -0
  28. data/lib/chef-dk/policyfile/chef_server_cookbook_source.rb +46 -0
  29. data/lib/chef-dk/policyfile/community_cookbook_source.rb +84 -0
  30. data/lib/chef-dk/policyfile/cookbook_sources.rb +20 -0
  31. data/lib/chef-dk/policyfile/cookbook_spec.rb +96 -0
  32. data/lib/chef-dk/policyfile/dsl.rb +148 -0
  33. data/lib/chef-dk/policyfile/null_cookbook_source.rb +37 -0
  34. data/lib/chef-dk/policyfile_compiler.rb +217 -0
  35. data/lib/chef-dk/policyfile_lock.rb +243 -0
  36. data/lib/chef-dk/shell_out.rb +36 -0
  37. data/lib/chef-dk/skeletons/code_generator/files/default/Berksfile +3 -0
  38. data/lib/chef-dk/skeletons/code_generator/files/default/chefignore +96 -0
  39. data/lib/chef-dk/skeletons/code_generator/files/default/converge_spec.rb +9 -0
  40. data/lib/chef-dk/skeletons/code_generator/files/default/gitignore +16 -0
  41. data/lib/chef-dk/skeletons/code_generator/files/default/spec_helper.rb +8 -0
  42. data/lib/chef-dk/skeletons/code_generator/metadata.rb +8 -0
  43. data/lib/chef-dk/skeletons/code_generator/recipes/app.rb +65 -0
  44. data/lib/chef-dk/skeletons/code_generator/recipes/attribute.rb +12 -0
  45. data/lib/chef-dk/skeletons/code_generator/recipes/cookbook.rb +50 -0
  46. data/lib/chef-dk/skeletons/code_generator/recipes/cookbook_file.rb +24 -0
  47. data/lib/chef-dk/skeletons/code_generator/recipes/lwrp.rb +23 -0
  48. data/lib/chef-dk/skeletons/code_generator/recipes/recipe.rb +9 -0
  49. data/lib/chef-dk/skeletons/code_generator/recipes/template.rb +32 -0
  50. data/lib/chef-dk/skeletons/code_generator/templates/default/README.md.erb +4 -0
  51. data/lib/chef-dk/skeletons/code_generator/templates/default/attribute.rb.erb +0 -0
  52. data/lib/chef-dk/skeletons/code_generator/templates/default/cookbook_file.erb +0 -0
  53. data/lib/chef-dk/skeletons/code_generator/templates/default/default_recipe.rb.erb +8 -0
  54. data/lib/chef-dk/skeletons/code_generator/templates/default/kitchen.yml.erb +16 -0
  55. data/lib/chef-dk/skeletons/code_generator/templates/default/metadata.rb.erb +8 -0
  56. data/lib/chef-dk/skeletons/code_generator/templates/default/provider.rb.erb +0 -0
  57. data/lib/chef-dk/skeletons/code_generator/templates/default/recipe.rb.erb +0 -0
  58. data/lib/chef-dk/skeletons/code_generator/templates/default/resource.rb.erb +0 -0
  59. data/lib/chef-dk/skeletons/code_generator/templates/default/template.erb +0 -0
  60. data/lib/chef-dk/version.rb +20 -0
  61. data/spec/shared/setup_git_cookbooks.rb +53 -0
  62. data/spec/spec_helper.rb +32 -0
  63. data/spec/test_helpers.rb +59 -0
  64. data/spec/unit/chef_runner_spec.rb +70 -0
  65. data/spec/unit/cli_spec.rb +151 -0
  66. data/spec/unit/command/base_spec.rb +88 -0
  67. data/spec/unit/command/exec_spec.rb +123 -0
  68. data/spec/unit/command/generate_spec.rb +102 -0
  69. data/spec/unit/command/generator_commands_spec.rb +504 -0
  70. data/spec/unit/command/shell_init_spec.rb +109 -0
  71. data/spec/unit/command/verify_spec.rb +311 -0
  72. data/spec/unit/commands_map_spec.rb +57 -0
  73. data/spec/unit/component_test_spec.rb +126 -0
  74. data/spec/unit/cookbook_metadata_spec.rb +62 -0
  75. data/spec/unit/cookbook_profiler/git_spec.rb +127 -0
  76. data/spec/unit/cookbook_profiler/identifiers_spec.rb +79 -0
  77. data/spec/unit/fixtures/chef-runner-cookbooks/test_cookbook/recipes/recipe_one.rb +9 -0
  78. data/spec/unit/fixtures/chef-runner-cookbooks/test_cookbook/recipes/recipe_two.rb +9 -0
  79. data/spec/unit/fixtures/command/cli_test_command.rb +26 -0
  80. data/spec/unit/fixtures/command/explicit_path_example.rb +7 -0
  81. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/.kitchen.yml +16 -0
  82. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/Berksfile +3 -0
  83. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/README.md +4 -0
  84. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/chefignore +96 -0
  85. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/metadata.rb +8 -0
  86. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/recipes/default.rb +8 -0
  87. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/.kitchen.yml +16 -0
  88. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/Berksfile +3 -0
  89. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/README.md +4 -0
  90. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/chefignore +96 -0
  91. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/metadata.rb +8 -0
  92. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/recipes/default.rb +8 -0
  93. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/.kitchen.yml +16 -0
  94. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/Berksfile +3 -0
  95. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/README.md +4 -0
  96. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/chefignore +96 -0
  97. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/metadata.rb +8 -0
  98. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/recipes/default.rb +8 -0
  99. data/spec/unit/fixtures/cookbooks_api/pruned_small_universe.json +1322 -0
  100. data/spec/unit/fixtures/cookbooks_api/small_universe.json +2987 -0
  101. data/spec/unit/fixtures/cookbooks_api/universe.json +1 -0
  102. data/spec/unit/fixtures/cookbooks_api/update_fixtures.rb +36 -0
  103. data/spec/unit/fixtures/dev_cookbooks/README.md +16 -0
  104. data/spec/unit/fixtures/dev_cookbooks/bar-cookbook.gitbundle +0 -0
  105. data/spec/unit/fixtures/eg_omnibus_dir/missing_apps/bin/.keep +0 -0
  106. data/spec/unit/fixtures/eg_omnibus_dir/missing_apps/embedded/.keep +0 -0
  107. data/spec/unit/fixtures/eg_omnibus_dir/missing_apps/embedded/bin/.keep +0 -0
  108. data/spec/unit/fixtures/eg_omnibus_dir/missing_component/bin/.keep +0 -0
  109. data/spec/unit/fixtures/eg_omnibus_dir/missing_component/embedded/apps/berkshelf/.keep +0 -0
  110. data/spec/unit/fixtures/eg_omnibus_dir/missing_component/embedded/apps/test-kitchen/.keep +0 -0
  111. data/spec/unit/fixtures/eg_omnibus_dir/missing_component/embedded/bin/.keep +0 -0
  112. data/spec/unit/fixtures/eg_omnibus_dir/valid/bin/.keep +0 -0
  113. data/spec/unit/fixtures/eg_omnibus_dir/valid/embedded/apps/berkshelf/integration_test +2 -0
  114. data/spec/unit/fixtures/eg_omnibus_dir/valid/embedded/apps/berkshelf/verify_me +5 -0
  115. data/spec/unit/fixtures/eg_omnibus_dir/valid/embedded/apps/chef-dk/.keep +0 -0
  116. data/spec/unit/fixtures/eg_omnibus_dir/valid/embedded/apps/chef/verify_me +3 -0
  117. data/spec/unit/fixtures/eg_omnibus_dir/valid/embedded/apps/test-kitchen/verify_me +2 -0
  118. data/spec/unit/fixtures/eg_omnibus_dir/valid/embedded/bin/.keep +0 -0
  119. data/spec/unit/fixtures/example_app/Policyfile.rb +0 -0
  120. data/spec/unit/fixtures/example_cookbook/.gitignore +17 -0
  121. data/spec/unit/fixtures/example_cookbook/.kitchen.yml +16 -0
  122. data/spec/unit/fixtures/example_cookbook/Berksfile +3 -0
  123. data/spec/unit/fixtures/example_cookbook/README.md +4 -0
  124. data/spec/unit/fixtures/example_cookbook/chefignore +96 -0
  125. data/spec/unit/fixtures/example_cookbook/metadata.rb +8 -0
  126. data/spec/unit/fixtures/example_cookbook/recipes/default.rb +8 -0
  127. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/.kitchen.yml +16 -0
  128. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/Berksfile +3 -0
  129. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/README.md +4 -0
  130. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/chefignore +96 -0
  131. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/metadata.rb +8 -0
  132. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/recipes/default.rb +8 -0
  133. data/spec/unit/fixtures/local_path_cookbooks/metadata-missing/README.md +2 -0
  134. data/spec/unit/policyfile/chef_server_cookbook_source_spec.rb +34 -0
  135. data/spec/unit/policyfile/community_cookbook_source_spec.rb +51 -0
  136. data/spec/unit/policyfile/cookbook_spec_spec.rb +200 -0
  137. data/spec/unit/policyfile/null_cookbook_source_spec.rb +35 -0
  138. data/spec/unit/policyfile_builder_spec.rb +489 -0
  139. data/spec/unit/policyfile_demands_spec.rb +484 -0
  140. data/spec/unit/policyfile_evaluation_spec.rb +284 -0
  141. data/spec/unit/shell_out_spec.rb +34 -0
  142. metadata +422 -0
@@ -0,0 +1,80 @@
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 'chef-dk/command/base'
19
+ require 'mixlib/shellout'
20
+
21
+ module ChefDK
22
+ module Command
23
+ class ShellInit < ChefDK::Command::Base
24
+
25
+ SUPPORTED_SHELLS = %w[ bash zsh sh ].map(&:freeze).freeze
26
+
27
+ banner(<<-HELP)
28
+ Usage: chef shell-init
29
+
30
+ `chef shell-init` modifies your shell environment to make ChefDK your default
31
+ ruby.
32
+
33
+ To enable for just the current shell session:
34
+
35
+ eval "$(chef shell-init SHELL_NAME)"
36
+
37
+ To permanently enable:
38
+
39
+ echo 'eval "$(chef shell-init SHELL_NAME)"' >> ~/.YOUR_SHELL_RC_FILE
40
+
41
+ OPTIONS:
42
+
43
+ HELP
44
+
45
+ option :omnibus_dir,
46
+ :long => "--omnibus-dir OMNIBUS_DIR",
47
+ :description => "Alternate path to omnibus install (used for testing)"
48
+
49
+ def omnibus_root
50
+ config[:omnibus_dir] || super
51
+ end
52
+
53
+ def run(argv)
54
+ # Currently we don't have any shell-specific features, so we ignore the
55
+ # shell name. We'll need it if we add completion.
56
+ remaining_args = parse_options(argv)
57
+ shell_name = remaining_args.first
58
+ if shell_name.nil?
59
+ err("Please specify what shell you are using\n")
60
+ err(opt_parser.to_s)
61
+ return 1
62
+ elsif !SUPPORTED_SHELLS.include?(shell_name)
63
+ err("Shell `#{shell_name}' is not currently supported")
64
+ err("Supported shells are: #{SUPPORTED_SHELLS.join(' ')}")
65
+ return 1
66
+ end
67
+
68
+ env = omnibus_env.dup
69
+ path = env.delete("PATH")
70
+ msg("export PATH=#{path}")
71
+ env.each do |var_name, value|
72
+ msg("export #{var_name}=#{value}")
73
+ end
74
+ 0
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+
@@ -0,0 +1,226 @@
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 'chef-dk/command/base'
19
+ require 'chef-dk/exceptions'
20
+ require 'chef-dk/component_test'
21
+
22
+ module ChefDK
23
+ module Command
24
+ class Verify < ChefDK::Command::Base
25
+
26
+
27
+ banner "Usage: chef verify [component, ...] [options]"
28
+
29
+ option :omnibus_dir,
30
+ :long => "--omnibus-dir OMNIBUS_DIR",
31
+ :description => "Alternate path to omnibus install (used for testing)"
32
+
33
+ option :unit,
34
+ :long => "--unit",
35
+ :description => "Run bundled app unit tests (only smoke tests run by default)"
36
+
37
+ option :integration,
38
+ :long => "--integration",
39
+ :description => "Run integration tests. Possibly dangerous, for development systems only"
40
+
41
+ option :verbose,
42
+ :long => "--verbose",
43
+ :description => "Display all test output, not just failing tests"
44
+
45
+ class << self
46
+ def add_component(name, _delete_me=nil)
47
+ component = ComponentTest.new(name)
48
+ yield component if block_given? #delete this conditional
49
+ component_map[name] = component
50
+ end
51
+
52
+ def component(name)
53
+ component_map[name]
54
+ end
55
+
56
+ def components
57
+ component_map.values
58
+ end
59
+
60
+ def component_map
61
+ @component_map ||= {}
62
+ end
63
+ end
64
+
65
+ def components
66
+ self.class.components
67
+ end
68
+
69
+ #
70
+ # Components included in Chef Development kit:
71
+ # :base_dir => Relative path of the component w.r.t. omnibus_apps_dir
72
+ # :test_cmd => Test command to be launched for the component
73
+ #
74
+ add_component "berkshelf" do |c|
75
+ c.base_dir = "berkshelf"
76
+ # For berks the real command to run is "bundle exec thor spec:ci"
77
+ # We can't run it right now since graphviz specs are included in the
78
+ # test suite by default. We will be able to switch to that command when/if
79
+ # Graphviz is added to omnibus.
80
+ c.unit_test { sh("bundle exec rspec --color --format progress spec/unit --tag ~graphviz") }
81
+ c.integration_test { sh("bundle exec cucumber --color --format progress --tags ~@no_run --tags ~@spawn --tags ~@graphviz --strict") }
82
+
83
+ c.smoke_test do
84
+ tmpdir do |cwd|
85
+ FileUtils.touch(File.join(cwd,"Berksfile"))
86
+ sh("berks install", cwd: cwd)
87
+ end
88
+ end
89
+ end
90
+
91
+ add_component "test-kitchen" do |c|
92
+ c.base_dir = "test-kitchen"
93
+ c.unit_test { sh("bundle exec rake unit") }
94
+ c.integration_test { sh("bundle exec rake features") }
95
+ c.smoke_test { run_in_tmpdir("kitchen init") }
96
+ end
97
+
98
+ add_component "chef-client" do |c|
99
+ c.base_dir = "chef"
100
+ c.unit_test { sh("bundle exec rspec -fp spec/unit") }
101
+ c.integration_test { sh("bundle exec rspec -fp spec/integration spec/functional") }
102
+
103
+ c.smoke_test do
104
+ tmpdir do |cwd|
105
+ FileUtils.touch(File.join(cwd, "apply.rb"))
106
+ sh("chef-apply apply.rb", cwd: cwd)
107
+ end
108
+ end
109
+ end
110
+
111
+ add_component "chef-dk" do |c|
112
+ c.base_dir = "chef-dk"
113
+ c.unit_test { sh("bundle exec rspec") }
114
+ c.smoke_test { run_in_tmpdir("chef generate cookbook example") }
115
+ end
116
+
117
+ attr_reader :verification_threads
118
+ attr_reader :verification_results
119
+ attr_reader :verification_status
120
+
121
+ def initialize
122
+ super
123
+ @verification_threads = [ ]
124
+ @verification_results = [ ]
125
+ @verification_status = 0
126
+ end
127
+
128
+ def run(params = [ ])
129
+ @components_filter = parse_options(params)
130
+
131
+ validate_components!
132
+ invoke_tests
133
+ wait_for_tests
134
+ report_results
135
+
136
+ verification_status
137
+ end
138
+
139
+ def omnibus_root
140
+ config[:omnibus_dir] || super
141
+ end
142
+
143
+ def validate_components!
144
+ components.each do |component|
145
+ component.omnibus_root = omnibus_root
146
+ component.assert_present!
147
+ end
148
+ end
149
+
150
+ def components_to_test
151
+ if @components_filter.empty?
152
+ components
153
+ else
154
+ components.select do |component|
155
+ @components_filter.include?(component.name.to_s)
156
+ end
157
+ end
158
+ end
159
+
160
+ def invoke_tests
161
+ components_to_test.each do |component|
162
+ # Run the component specs in parallel
163
+ verification_threads << Thread.new do
164
+
165
+ results = []
166
+
167
+ results << component.run_smoke_test
168
+
169
+ if config[:unit]
170
+ results << component.run_unit_test
171
+ end
172
+
173
+ if config[:integration]
174
+ results << component.run_integration_test
175
+ end
176
+
177
+ if results.any? {|r| r.exitstatus != 0 }
178
+ component_status = 1
179
+ @verification_status = 1
180
+ else
181
+ component_status = 0
182
+ end
183
+
184
+ {
185
+ :component => component,
186
+ :results => results,
187
+ :component_status => component_status
188
+ }
189
+ end
190
+
191
+ msg("Running verification for component '#{component.name}'")
192
+ end
193
+ end
194
+
195
+ def wait_for_tests
196
+ while !verification_threads.empty?
197
+ verification_threads.each do |t|
198
+ if t.join(1)
199
+ verification_threads.delete t
200
+ verification_results << t.value
201
+ t.value[:results].each do |result|
202
+ if config[:verbose] || t.value[:component_status] != 0
203
+ msg("")
204
+ msg(result.stdout)
205
+ msg(result.stderr) if result.stderr
206
+ end
207
+ end
208
+ else
209
+ $stdout.write "."
210
+ end
211
+ end
212
+ end
213
+ end
214
+
215
+ def report_results
216
+ msg("")
217
+ msg("---------------------------------------------")
218
+ verification_results.each do |result|
219
+ message = result[:component_status] == 0 ? "succeeded" : "failed"
220
+ msg("Verification of component '#{result[:component].name}' #{message}.")
221
+ end
222
+ end
223
+
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,115 @@
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
+ module ChefDK
19
+
20
+ # CommandsMap maintains a mapping of subcommand names to the files where
21
+ # those commands are defined and the classes that implement the commands.
22
+ #
23
+ # In ruby it's more typical to handle this sort of thing using conventions
24
+ # and metaprogramming. We've implemented this approach in the past and
25
+ # decided against it here:
26
+ # 1. Performance. As the CLI suite grows, you have to load more and more
27
+ # code, including dependencies that are installed by rubygems, etc. This gets
28
+ # slow, and CLI apps need to be fast.
29
+ # 2. You can workaround the above by having a convention mapping filename to
30
+ # command name, but then you have to do a lot of work to list all of the
31
+ # commands, which is actually a common thing to do.
32
+ # 3. Other ways to mitigate the performance issue (loading deps lazily) have
33
+ # their own complications and tradeoffs and don't fully solve the problem.
34
+ # 4. It's not actually that much work to maintain the mapping.
35
+ #
36
+ # ## Adding new commands globally:
37
+ #
38
+ # A "singleton-ish" instance of this class is stored as ChefDK.commands_map.
39
+ # You can configure a multiple commands at once in a block using
40
+ # ChefDK.commands, like so:
41
+ #
42
+ # ChefDK.commands do |c|
43
+ # # assigns `chef my-command` to the class ChefDK::Command::MyCommand.
44
+ # # The "require path" is inferred to be "chef-dk/command/my_command"
45
+ # c.builtin("my-command", :MyCommand)
46
+ #
47
+ # # Set the require path explicitly:
48
+ # c.builtin("weird-command", :WeirdoClass, require_path: "chef-dk/command/this_is_cray")
49
+ #
50
+ # # You can add a description that will show up in `chef -h` output (recommended):
51
+ # c.builtin("documented-cmd", :DocumentedCmd, desc: "A short description")
52
+ # end
53
+ #
54
+ class CommandsMap
55
+ NULL_ARG = Object.new
56
+
57
+ CommandSpec = Struct.new(:name, :constant_name, :require_path, :description)
58
+
59
+ class CommandSpec
60
+
61
+ def instantiate
62
+ require require_path
63
+ command_class = ChefDK::Command.const_get(constant_name)
64
+ command_class.new
65
+ end
66
+
67
+ end
68
+
69
+ attr_reader :command_specs
70
+
71
+ def initialize
72
+ @command_specs = {}
73
+ end
74
+
75
+ def builtin(name, constant_name, require_path: NULL_ARG, desc: "")
76
+ if null?(require_path)
77
+ snake_case_path = name.gsub("-", "_")
78
+ require_path = "chef-dk/command/#{snake_case_path}"
79
+ end
80
+ command_specs[name] = CommandSpec.new(name, constant_name, require_path, desc)
81
+ end
82
+
83
+ def instantiate(name)
84
+ spec_for(name).instantiate
85
+ end
86
+
87
+ def have_command?(name)
88
+ command_specs.key?(name)
89
+ end
90
+
91
+ def command_names
92
+ command_specs.keys
93
+ end
94
+
95
+ def spec_for(name)
96
+ command_specs[name]
97
+ end
98
+
99
+ private
100
+
101
+ def null?(argument)
102
+ argument.equal?(NULL_ARG)
103
+ end
104
+ end
105
+
106
+ def self.commands_map
107
+ @commands_map ||= CommandsMap.new
108
+ end
109
+
110
+ def self.commands
111
+ yield commands_map
112
+ end
113
+ end
114
+
115
+
@@ -0,0 +1,142 @@
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 'chef-dk/exceptions'
19
+ require 'chef-dk/helpers'
20
+
21
+ module ChefDK
22
+ class ComponentTest
23
+
24
+ class NullTestResult
25
+ def exitstatus
26
+ 0
27
+ end
28
+
29
+ def stdout
30
+ ""
31
+ end
32
+
33
+ def stderr
34
+ ""
35
+ end
36
+ end
37
+
38
+ DEFAULT_TEST = lambda { |context| NullTestResult.new }
39
+
40
+ include Helpers
41
+
42
+ attr_accessor :base_dir
43
+
44
+ attr_writer :omnibus_root
45
+
46
+ attr_reader :name
47
+
48
+ def initialize(name)
49
+ @name = name
50
+ @unit_test = DEFAULT_TEST
51
+ @integration_test = DEFAULT_TEST
52
+ @smoke_test = DEFAULT_TEST
53
+ end
54
+
55
+ def unit_test(&test_block)
56
+ @unit_test = test_block
57
+ end
58
+
59
+ def run_unit_test
60
+ instance_eval(&@unit_test)
61
+ end
62
+
63
+ def integration_test(&test_block)
64
+ @integration_test = test_block
65
+ end
66
+
67
+ def run_integration_test
68
+ instance_eval(&@integration_test)
69
+ end
70
+
71
+ def smoke_test(&test_block)
72
+ @smoke_test = test_block
73
+ end
74
+
75
+ def run_smoke_test
76
+ instance_eval(&@smoke_test)
77
+ end
78
+
79
+ def sh(command, options={})
80
+ combined_opts = default_command_options.merge(options)
81
+
82
+ # Env is a hash, so it needs to be merged separately
83
+ if options.key?(:env)
84
+ combined_opts[:env] = default_command_options[:env].merge(options[:env])
85
+ end
86
+ system_command(command, combined_opts)
87
+ end
88
+
89
+ def run_in_tmpdir(command, options={})
90
+ tmpdir do |dir|
91
+ options[:cwd] = dir
92
+ sh(command, options)
93
+ end
94
+ end
95
+
96
+ def tmpdir
97
+ Dir.mktmpdir do |tmpdir|
98
+ yield tmpdir
99
+ end
100
+ end
101
+
102
+ def assert_present!
103
+ unless File.exists?( component_path )
104
+ raise MissingComponentError.new(name)
105
+ end
106
+ end
107
+
108
+ def default_command_options
109
+ {
110
+ :cwd => component_path,
111
+ :env => {
112
+ # Add the embedded/bin to the PATH so that bundle executable can
113
+ # be found while running the tests.
114
+ path_variable_key => omnibus_path
115
+ },
116
+ :timeout => 3600
117
+ }
118
+ end
119
+
120
+ def component_path
121
+ File.join(omnibus_apps_dir, base_dir)
122
+ end
123
+
124
+ def base_dir
125
+ @base_dir or raise "`base_dir` must be defined for component `#{name}`"
126
+ end
127
+
128
+ def omnibus_root
129
+ @omnibus_root or raise "`omnibus_root` must be set before running tests"
130
+ end
131
+
132
+ def omnibus_path
133
+ [omnibus_bin_dir, omnibus_embedded_bin_dir, ENV['PATH']].join(File::PATH_SEPARATOR)
134
+ end
135
+
136
+ def path_variable_key
137
+ ENV.keys.grep(/\Apath\Z/i).first
138
+ end
139
+
140
+ end
141
+ end
142
+