foodcritic 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +691 -0
- data/LICENSE +21 -0
- data/README.md +28 -0
- data/chef_dsl_metadata/chef_0.10.10.json +22 -0
- data/chef_dsl_metadata/chef_10.12.0.json +22 -0
- data/chef_dsl_metadata/chef_10.14.0.json +22 -0
- data/chef_dsl_metadata/chef_10.14.2.json +22 -0
- data/chef_dsl_metadata/chef_10.14.4.json +22 -0
- data/chef_dsl_metadata/chef_10.16.0.json +22 -0
- data/chef_dsl_metadata/chef_10.16.2.json +22 -0
- data/chef_dsl_metadata/chef_10.16.4.json +22 -0
- data/chef_dsl_metadata/chef_10.16.6.json +22 -0
- data/chef_dsl_metadata/chef_10.18.0.json +22 -0
- data/chef_dsl_metadata/chef_10.18.2.json +22 -0
- data/chef_dsl_metadata/chef_10.20.0.json +22 -0
- data/chef_dsl_metadata/chef_10.22.0.json +22 -0
- data/chef_dsl_metadata/chef_10.24.0.json +22 -0
- data/chef_dsl_metadata/chef_10.24.4.json +8460 -0
- data/chef_dsl_metadata/chef_10.26.0.json +8460 -0
- data/chef_dsl_metadata/chef_11.0.0.json +22 -0
- data/chef_dsl_metadata/chef_11.2.0.json +22 -0
- data/chef_dsl_metadata/chef_11.4.0.json +22 -0
- data/chef_dsl_metadata/chef_11.4.2.json +8794 -0
- data/chef_dsl_metadata/chef_11.4.4.json +8794 -0
- data/features/002_check_string_interpolation.feature +45 -0
- data/features/003_check_for_chef_server.feature +56 -0
- data/features/004_check_service_resource_used.feature +53 -0
- data/features/005_check_for_resource_repetition.feature +64 -0
- data/features/006_check_file_mode.feature +35 -0
- data/features/007_check_for_undeclared_recipe_dependencies.feature +71 -0
- data/features/008_check_for_boilerplate_metadata.feature +25 -0
- data/features/009_check_for_unrecognised_resource_attributes.feature +77 -0
- data/features/010_check_search_syntax.feature +20 -0
- data/features/011_check_for_markdown_readme.feature +20 -0
- data/features/012_check_for_deprecated_readme_format.feature +20 -0
- data/features/013_check_for_hardcoded_tmpdir.feature +25 -0
- data/features/014_check_for_long_ruby_blocks.feature +30 -0
- data/features/015_check_for_definitions.feature +21 -0
- data/features/016_check_for_no_lwrp_default_action.feature +20 -0
- data/features/017_check_for_no_lwrp_notifications.feature +25 -0
- data/features/018_check_for_old_lwrp_notification_syntax.feature +25 -0
- data/features/019_check_for_consistent_node_access.feature +107 -0
- data/features/021_check_for_dodgy_lwrp_conditions.feature +28 -0
- data/features/022_check_for_dodgy_conditions_within_loop.feature +28 -0
- data/features/023_check_for_condition_around_resource.feature +52 -0
- data/features/024_check_for_missing_platforms.feature +43 -0
- data/features/025_check_for_deprecated_gem_install.feature +30 -0
- data/features/026_check_for_conditional_block_string.feature +20 -0
- data/features/027_check_for_internal_attribute_use.feature +22 -0
- data/features/028_check_for_incorrect_platform_method.feature +20 -0
- data/features/029_check_for_no_leading_cookbook_name.feature +18 -0
- data/features/030_check_for_debugger_breakpoints.feature +25 -0
- data/features/031_check_for_metadata_existence.feature +15 -0
- data/features/032_check_for_invalid_notification_timing.feature +22 -0
- data/features/033_check_for_missing_template.feature +75 -0
- data/features/034_check_for_unused_template_variables.feature +37 -0
- data/features/037_check_for_invalid_notification_action.feature +34 -0
- data/features/038_check_for_invalid_action.feature +51 -0
- data/features/039_check_for_key_access_to_node_methods.feature +33 -0
- data/features/040_check_raw_git_usage.feature +37 -0
- data/features/041_check_raw_download.feature +26 -0
- data/features/042_check_for_deprecated_require_recipe.feature +15 -0
- data/features/043_check_for_old_notification_style.feature +35 -0
- data/features/044_check_for_bare_attribute_keys.feature +43 -0
- data/features/045_check_for_cookbook_name_in_metadata.feature +20 -0
- data/features/046_check_for_assign_unless_nil_attributes.feature +21 -0
- data/features/build_framework_support.feature +99 -0
- data/features/checking_all_types_of_file.feature +40 -0
- data/features/choose_rules_to_apply.feature +49 -0
- data/features/command_line_help.feature +43 -0
- data/features/continuous_integration_support.feature +35 -0
- data/features/ignore_via_line_comments.feature +51 -0
- data/features/include_custom_rules.feature +29 -0
- data/features/individual_file.feature +12 -0
- data/features/limit_rules_to_specific_versions.feature +65 -0
- data/features/multiple_paths.feature +11 -0
- data/features/show_lines_matched.feature +20 -0
- data/features/sort_warnings.feature +10 -0
- data/features/specify_search_grammar.feature +25 -0
- data/features/step_definitions/cookbook_steps.rb +1791 -0
- data/features/support/command_helpers.rb +312 -0
- data/features/support/cookbook_helpers.rb +495 -0
- data/features/support/env.rb +11 -0
- data/lib/foodcritic.rb +0 -1
- data/lib/foodcritic/api.rb +3 -2
- data/lib/foodcritic/command_line.rb +4 -0
- data/lib/foodcritic/linter.rb +29 -6
- data/lib/foodcritic/output.rb +74 -26
- data/lib/foodcritic/rules.rb +6 -5
- data/lib/foodcritic/version.rb +1 -1
- data/man/foodcritic.1 +58 -0
- data/man/foodcritic.1.ronn +57 -0
- data/spec/foodcritic/api_spec.rb +1615 -0
- data/spec/foodcritic/chef_spec.rb +66 -0
- data/spec/foodcritic/command_line_spec.rb +51 -0
- data/spec/foodcritic/domain_spec.rb +24 -0
- data/spec/foodcritic/linter_spec.rb +91 -0
- data/spec/foodcritic/template_spec.rb +49 -0
- data/spec/regression/cookbooks.txt +135 -0
- data/spec/regression/expected-output.txt +443 -0
- data/spec/regression/regression_spec.rb +17 -0
- data/spec/regression_helpers.rb +37 -0
- data/spec/spec_helper.rb +10 -0
- metadata +87 -24
@@ -0,0 +1,312 @@
|
|
1
|
+
module FoodCritic
|
2
|
+
|
3
|
+
# Helpers for asserting that the correct warnings are displayed.
|
4
|
+
#
|
5
|
+
# Unless the environment variable FC_FORK_PROCESS is set to 'true' then the features will be run in the same process.
|
6
|
+
module CommandHelpers
|
7
|
+
|
8
|
+
# The warning codes and messages displayed to the end user.
|
9
|
+
WARNINGS = {
|
10
|
+
'FC002' => 'Avoid string interpolation where not required',
|
11
|
+
'FC003' => 'Check whether you are running with chef server before using server-specific features',
|
12
|
+
'FC004' => 'Use a service resource to start and stop services',
|
13
|
+
'FC005' => 'Avoid repetition of resource declarations',
|
14
|
+
'FC006' => 'Mode should be quoted or fully specified when setting file permissions',
|
15
|
+
'FC007' => 'Ensure recipe dependencies are reflected in cookbook metadata',
|
16
|
+
'FC008' => 'Generated cookbook metadata needs updating',
|
17
|
+
'FC009' => 'Resource attribute not recognised',
|
18
|
+
'FC010' => 'Invalid search syntax',
|
19
|
+
'FC011' => 'Missing README in markdown format',
|
20
|
+
'FC012' => 'Use Markdown for README rather than RDoc',
|
21
|
+
'FC013' => 'Use file_cache_path rather than hard-coding tmp paths',
|
22
|
+
'FC014' => 'Consider extracting long ruby_block to library',
|
23
|
+
'FC015' => 'Consider converting definition to a LWRP',
|
24
|
+
'FC016' => 'LWRP does not declare a default action',
|
25
|
+
'FC017' => 'LWRP does not notify when updated',
|
26
|
+
'FC018' => 'LWRP uses deprecated notification syntax',
|
27
|
+
'FC019' => 'Access node attributes in a consistent manner',
|
28
|
+
'FC021' => 'Resource condition in provider may not behave as expected',
|
29
|
+
'FC022' => 'Resource condition within loop may not behave as expected',
|
30
|
+
'FC023' => 'Prefer conditional attributes',
|
31
|
+
'FC024' => 'Consider adding platform equivalents',
|
32
|
+
'FC025' => 'Prefer chef_gem to compile-time gem install',
|
33
|
+
'FC026' => 'Conditional execution block attribute contains only string',
|
34
|
+
'FC027' => 'Resource sets internal attribute',
|
35
|
+
'FC028' => 'Incorrect #platform? usage',
|
36
|
+
'FC029' => 'No leading cookbook name in recipe metadata',
|
37
|
+
'FC030' => 'Cookbook contains debugger breakpoints',
|
38
|
+
'FC031' => 'Cookbook without metadata file',
|
39
|
+
'FC032' => 'Invalid notification timing',
|
40
|
+
'FC033' => 'Missing template',
|
41
|
+
'FC034' => 'Unused template variables',
|
42
|
+
'FC037' => 'Invalid notification action',
|
43
|
+
'FC038' => 'Invalid resource action',
|
44
|
+
'FC039' => 'Node method cannot be accessed with key',
|
45
|
+
'FC040' => 'Execute resource used to run git commands',
|
46
|
+
'FC041' => 'Execute resource used to run curl or wget commands',
|
47
|
+
'FC042' => 'Prefer include_recipe to require_recipe',
|
48
|
+
'FC043' => 'Prefer new notification syntax',
|
49
|
+
'FC044' => 'Avoid bare attribute keys',
|
50
|
+
'FC045' => 'Consider setting cookbook name in metadata',
|
51
|
+
'FC046' => 'Attribute assignment uses assign unless nil',
|
52
|
+
'FCTEST001' => 'Test Rule'
|
53
|
+
}
|
54
|
+
|
55
|
+
# If the cucumber features should run foodcritic in the same process or spawn a separate process.
|
56
|
+
def self.running_in_process?
|
57
|
+
! (ENV.has_key?('FC_FORK_PROCESS') and ENV['FC_FORK_PROCESS'] == true.to_s)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Capture an error expected when calling a command.
|
61
|
+
def capture_error
|
62
|
+
begin
|
63
|
+
yield
|
64
|
+
@error = all_output unless last_exit_status == 0
|
65
|
+
rescue => @error
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Return the last error captured
|
70
|
+
#
|
71
|
+
# @return [String] The last error captured
|
72
|
+
def last_error
|
73
|
+
@error.respond_to?(:message) ? @error.message : @error
|
74
|
+
end
|
75
|
+
|
76
|
+
# Expect a line of context
|
77
|
+
#
|
78
|
+
# @param [Number] line_no The line number
|
79
|
+
# @param [String] text The text of the matching line
|
80
|
+
def expect_line_shown(line_no, text)
|
81
|
+
expect_output %r{^ +#{Regexp.escape(line_no.to_s)}\|#{Regexp.escape(text)}$}
|
82
|
+
end
|
83
|
+
|
84
|
+
# Expect a warning to be included in the command output.
|
85
|
+
#
|
86
|
+
# @param [String] code The warning code to check for.
|
87
|
+
# @param [Hash] options The warning options.
|
88
|
+
# @option options [Integer] :line The line number the warning should appear on - nil for any line.
|
89
|
+
# @option options [Boolean] :expect_warning If false then assert that a warning is NOT present
|
90
|
+
# @option options [String] :file The path to the file the warning should be raised against
|
91
|
+
# @option options [Symbol] :file_type Alternative to specifying file name. One of: :attributes, :definition,
|
92
|
+
# :metadata, :provider, :resource
|
93
|
+
def expect_warning(code, options={})
|
94
|
+
if options.has_key?(:file_type)
|
95
|
+
options[:file] = {:attributes => 'attributes/default.rb', :definition => 'definitions/apache_site.rb',
|
96
|
+
:metadata => 'metadata.rb', :provider => 'providers/site.rb',
|
97
|
+
:resource => 'resources/site.rb', :libraries => 'libraries/lib.rb'}[options[:file_type]]
|
98
|
+
end
|
99
|
+
options = {:line => 1, :expect_warning => true, :file => 'recipes/default.rb'}.merge!(options)
|
100
|
+
if options[:warning_only]
|
101
|
+
warning = "#{code}: #{WARNINGS[code]}"
|
102
|
+
else
|
103
|
+
warning = "#{code}: #{WARNINGS[code]}: cookbooks/example/#{options[:file]}:#{options[:line]}#{"\n" if ! options[:line].nil?}"
|
104
|
+
end
|
105
|
+
options[:expect_warning] ? expect_output(warning) : expect_no_output(warning)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Expect a warning not to be included in the command output.
|
109
|
+
#
|
110
|
+
# @see CommandHelpers#expect_warning
|
111
|
+
def expect_no_warning(code, options={:expect_warning => false})
|
112
|
+
expect_warning(code, options)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Expect a command line option / switch to be included in the usage.
|
116
|
+
#
|
117
|
+
# @param [String] short_switch The short version of the switch
|
118
|
+
# @param [String] long_switch The long descriptive version of the switch
|
119
|
+
# @param [String] description The description of the switch
|
120
|
+
def expect_usage_option(short_switch, long_switch, description)
|
121
|
+
expected_switch = "-#{Regexp.escape(short_switch)}, --#{Regexp.escape(long_switch)}[ ]+#{Regexp.escape(description)}"
|
122
|
+
expect_output(Regexp.new(expected_switch))
|
123
|
+
end
|
124
|
+
|
125
|
+
# Assert that the usage message is displayed.
|
126
|
+
#
|
127
|
+
# @param [Boolean] is_exit_zero The exit code to check for.
|
128
|
+
def usage_displayed(is_exit_zero)
|
129
|
+
expect_output 'foodcritic [cookbook_paths]'
|
130
|
+
expect_usage_option('c', 'chef-version VERSION', 'Only check against rules valid for this version of Chef.')
|
131
|
+
expect_usage_option('f', 'epic-fail TAGS',
|
132
|
+
"Fail the build if any of the specified tags are matched ('any' -> fail on any match).")
|
133
|
+
expect_usage_option('t', 'tags TAGS', 'Only check against rules with the specified tags.')
|
134
|
+
expect_usage_option('C', '[no-]context', 'Show lines matched against rather than the default summary.')
|
135
|
+
expect_usage_option('I', 'include PATH', 'Additional rule file path(s) to load.')
|
136
|
+
expect_usage_option('S', 'search-grammar PATH', 'Specify grammar to use when validating search syntax.')
|
137
|
+
expect_usage_option('V', 'version', 'Display the foodcritic version.')
|
138
|
+
if is_exit_zero
|
139
|
+
assert_no_error_occurred
|
140
|
+
else
|
141
|
+
assert_error_occurred
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
# Helpers used when features are executed in-process.
|
148
|
+
module InProcessHelpers
|
149
|
+
|
150
|
+
# Assert that the output contains the specified warning.
|
151
|
+
#
|
152
|
+
# @param [String] output The warning to check for.
|
153
|
+
def expect_output(output)
|
154
|
+
if output.respond_to?(:~)
|
155
|
+
@review.should match(output)
|
156
|
+
else
|
157
|
+
@review.should include(output)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Assert that the output does not contain the specified warning.
|
162
|
+
#
|
163
|
+
# @param [String] output The output to check for.
|
164
|
+
def expect_no_output(output)
|
165
|
+
if output.respond_to?(:~)
|
166
|
+
@review.should_not match(output)
|
167
|
+
else
|
168
|
+
@review.should_not include(output)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Assert that an error occurred following a lint check.
|
173
|
+
def assert_error_occurred
|
174
|
+
@status.should_not == 0
|
175
|
+
end
|
176
|
+
|
177
|
+
# Assert that no error occurred following a lint check.
|
178
|
+
def assert_no_error_occurred
|
179
|
+
@status.should == 0
|
180
|
+
end
|
181
|
+
|
182
|
+
# Run a lint check with the provided command line arguments.
|
183
|
+
#
|
184
|
+
# @param [Array] cmd_args The command line arguments.
|
185
|
+
def run_lint(cmd_args)
|
186
|
+
in_current_dir do
|
187
|
+
show_context = cmd_args.include?('-C')
|
188
|
+
review, @status = FoodCritic::Linter.check(CommandLine.new(cmd_args))
|
189
|
+
@review =
|
190
|
+
if review.nil? || (review.respond_to?(:warnings) && review.warnings.empty?)
|
191
|
+
''
|
192
|
+
elsif show_context
|
193
|
+
ContextOutput.new.output(review)
|
194
|
+
else
|
195
|
+
"#{review.to_s}\n"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
# For use with steps that use bundler and rake. These will always be run
|
203
|
+
# via Aruba.
|
204
|
+
module BuildHelpers
|
205
|
+
|
206
|
+
# Assert the build outcome
|
207
|
+
#
|
208
|
+
# @param [Boolean] success True if the build should succeed
|
209
|
+
# @param [Array] warnings The warnings expected
|
210
|
+
def assert_build_result(success, warnings)
|
211
|
+
success ? assert_no_error_occurred : assert_error_occurred
|
212
|
+
warnings.each do |code|
|
213
|
+
expect_warning(code, :warning_only => true)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# Assert that warnings have not been raised against the test code which
|
218
|
+
# should have been excluded from linting.
|
219
|
+
def assert_no_test_warnings
|
220
|
+
all_output.split("\n").grep(/FC[0-9]+:/).map do |warn|
|
221
|
+
File.basename(File.dirname(warn.split(':').take(3).last.strip))
|
222
|
+
end.should_not include 'test'
|
223
|
+
end
|
224
|
+
|
225
|
+
# The available tasks for this build
|
226
|
+
#
|
227
|
+
# @return [Array] Task name and description
|
228
|
+
def build_tasks
|
229
|
+
all_output.split("\n").map do |task|
|
230
|
+
next unless task.start_with? 'rake'
|
231
|
+
task.split("#").map{|t| t.strip.sub(/^rake /, '')}
|
232
|
+
end.compact
|
233
|
+
end
|
234
|
+
|
235
|
+
# List the defined Rake tasks
|
236
|
+
def list_available_build_tasks
|
237
|
+
cd 'cookbooks/example'
|
238
|
+
unset_bundler_env_vars
|
239
|
+
run_simple 'bundle exec rake -T'
|
240
|
+
end
|
241
|
+
|
242
|
+
# Run a build for a Rakefile that uses the lint rake task
|
243
|
+
def run_build
|
244
|
+
cd 'cookbooks/example'
|
245
|
+
run_simple "bundle exec rake", false
|
246
|
+
end
|
247
|
+
|
248
|
+
# We want to avoid traversing vendored gems because of the unnecessary
|
249
|
+
# performance hit and because gems may contain deeply-nested code which
|
250
|
+
# will blow the stack on parsing.
|
251
|
+
def vendor_gems
|
252
|
+
cd 'cookbooks/example'
|
253
|
+
unset_bundler_env_vars
|
254
|
+
run_simple 'bundle install --path vendor/bundle'
|
255
|
+
cd '../..'
|
256
|
+
end
|
257
|
+
|
258
|
+
end
|
259
|
+
|
260
|
+
# Helpers used when features are executed out of process.
|
261
|
+
module ArubaHelpers
|
262
|
+
|
263
|
+
include BuildHelpers
|
264
|
+
|
265
|
+
# Assert that the output contains the specified warning.
|
266
|
+
#
|
267
|
+
# @param [String] output The output to check for.
|
268
|
+
def expect_output(output)
|
269
|
+
if output.respond_to?(:~)
|
270
|
+
assert_matching_output(output.to_s, all_output)
|
271
|
+
else
|
272
|
+
assert_partial_output(output, all_output)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
# Assert that the output does not contain the specified warning.
|
277
|
+
#
|
278
|
+
# @param [String] output The output to check for.
|
279
|
+
def expect_no_output(output)
|
280
|
+
if output.respond_to?(:~)
|
281
|
+
assert_matching_output('^((?!#{output}).)*$', all_output)
|
282
|
+
else
|
283
|
+
assert_no_partial_output(output, all_output)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# Assert that an error occurred following a lint check.
|
288
|
+
def assert_error_occurred
|
289
|
+
assert_not_exit_status 0
|
290
|
+
end
|
291
|
+
|
292
|
+
# Assert that no error occurred following a lint check.
|
293
|
+
def assert_no_error_occurred
|
294
|
+
assert_exit_status(0)
|
295
|
+
end
|
296
|
+
|
297
|
+
# Run a lint check with the provided command line arguments.
|
298
|
+
#
|
299
|
+
# @param [Array] cmd_args The command line arguments.
|
300
|
+
def run_lint(cmd_args)
|
301
|
+
run_simple(unescape("foodcritic #{cmd_args.join(' ')}"), false)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
end
|
306
|
+
|
307
|
+
World(FoodCritic::CommandHelpers)
|
308
|
+
if FoodCritic::CommandHelpers.running_in_process?
|
309
|
+
World(FoodCritic::InProcessHelpers)
|
310
|
+
else
|
311
|
+
World(FoodCritic::ArubaHelpers)
|
312
|
+
end
|
@@ -0,0 +1,495 @@
|
|
1
|
+
module FoodCritic
|
2
|
+
|
3
|
+
# Helper cookbook methods for use in your steps.
|
4
|
+
module CookbookHelpers
|
5
|
+
|
6
|
+
# Create an attributes file that references attributes with symbols
|
7
|
+
#
|
8
|
+
# @param [String] type The type of node attribute to write
|
9
|
+
def attributes_with_symbols(type)
|
10
|
+
write_attributes %Q{#{type}[:apache][:dir] = "/etc/apache2"}
|
11
|
+
end
|
12
|
+
|
13
|
+
# Create a Gemfile for a cookbook
|
14
|
+
def buildable_gemfile
|
15
|
+
write_file 'cookbooks/example/Gemfile', %q{
|
16
|
+
source :rubygems
|
17
|
+
gem 'rake'
|
18
|
+
gem 'foodcritic', :path => '../../../..'
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
# Create a cookbook that declares dependencies on external recipes.
|
23
|
+
#
|
24
|
+
# @param [Symbol] declaration_type The type of declaration - :brace or :bracket
|
25
|
+
def cookbook_declares_dependencies(declaration_type)
|
26
|
+
write_recipe %q{
|
27
|
+
include_recipe "foo::default"
|
28
|
+
include_recipe "bar::default"
|
29
|
+
include_recipe "baz::default"
|
30
|
+
}
|
31
|
+
if declaration_type == :brace
|
32
|
+
write_metadata %q{
|
33
|
+
%w{foo bar baz}.each{|cookbook| depends cookbook}
|
34
|
+
}
|
35
|
+
else
|
36
|
+
write_metadata %q{
|
37
|
+
%w{foo bar baz}.each do |cb|
|
38
|
+
depends cb
|
39
|
+
end
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Create a cookbook that will match the specified rules.
|
45
|
+
#
|
46
|
+
# @param [Array] codes The codes to match. Only FC002, FC003 and FC004 are supported.
|
47
|
+
def cookbook_that_matches_rules(codes)
|
48
|
+
recipe = ''
|
49
|
+
codes.each do |code|
|
50
|
+
if code == 'FC002'
|
51
|
+
recipe += %q{
|
52
|
+
directory "#{node['base_dir']}" do
|
53
|
+
action :create
|
54
|
+
end
|
55
|
+
}
|
56
|
+
elsif code == 'FC003'
|
57
|
+
recipe += %Q{nodes = search(:node, "hostname:[* TO *]")\n}
|
58
|
+
elsif code == 'FC004'
|
59
|
+
recipe += %q{
|
60
|
+
execute "stop-jetty" do
|
61
|
+
command "/etc/init.d/jetty6 stop"
|
62
|
+
action :run
|
63
|
+
end
|
64
|
+
}
|
65
|
+
elsif code == 'FC006'
|
66
|
+
recipe += %q{
|
67
|
+
directory "/var/lib/foo" do
|
68
|
+
mode 644
|
69
|
+
action :create
|
70
|
+
end
|
71
|
+
}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
write_recipe(recipe)
|
75
|
+
write_file('cookbooks/example/recipes/server.rb', '')
|
76
|
+
write_readme('Hello World') # Don't trigger FC011
|
77
|
+
write_metadata('name "example"') # Don't trigger FC031
|
78
|
+
end
|
79
|
+
|
80
|
+
# Create a cookbook with a LRWP
|
81
|
+
#
|
82
|
+
# @param [Hash] lwrp The options to use for the created LWRP
|
83
|
+
# @option lwrp [Symbol] :default_action One of :no_default_action, :ruby_default_action, :dsl_default_action
|
84
|
+
# @option lwrp [Symbol] :notifies One of :does_not_notify, :does_notify, :does_notify_without_parens, :deprecated_syntax, :class_variable
|
85
|
+
def cookbook_with_lwrp(lwrp)
|
86
|
+
lwrp = {:default_action => false, :notifies => :does_not_notify}.merge!(lwrp)
|
87
|
+
ruby_default_action = %q{
|
88
|
+
def initialize(*args)
|
89
|
+
super
|
90
|
+
@action = :create
|
91
|
+
end
|
92
|
+
}.strip
|
93
|
+
write_resource("site", %Q{
|
94
|
+
actions :create
|
95
|
+
attribute :name, :kind_of => String, :name_attribute => true
|
96
|
+
#{ruby_default_action if lwrp[:default_action] == :ruby_default_action}
|
97
|
+
#{'default_action :create' if lwrp[:default_action] == :dsl_default_action}
|
98
|
+
})
|
99
|
+
notifications = {:does_notify => 'new_resource.updated_by_last_action(true)',
|
100
|
+
:does_notify_without_parens => 'new_resource.updated_by_last_action true',
|
101
|
+
:deprecated_syntax => 'new_resource.updated = true',
|
102
|
+
:class_variable => '@updated = true'}
|
103
|
+
write_provider("site", %Q{
|
104
|
+
action :create do
|
105
|
+
log "Here is where I would create a site"
|
106
|
+
#{notifications[lwrp[:notifies]]}
|
107
|
+
end
|
108
|
+
})
|
109
|
+
end
|
110
|
+
|
111
|
+
# Create an cookbook with the maintainer specified in the metadata
|
112
|
+
#
|
113
|
+
# @param [String] name The maintainer name
|
114
|
+
# @param [String] email The maintainer email address
|
115
|
+
def cookbook_with_maintainer(name, email)
|
116
|
+
write_recipe %q{
|
117
|
+
#
|
118
|
+
# Cookbook Name:: example
|
119
|
+
# Recipe:: default
|
120
|
+
#
|
121
|
+
# Copyright 2011, YOUR_COMPANY_NAME
|
122
|
+
#
|
123
|
+
# All rights reserved - Do Not Redistribute
|
124
|
+
#
|
125
|
+
}
|
126
|
+
|
127
|
+
fields = {}
|
128
|
+
fields['maintainer'] = name unless name.nil?
|
129
|
+
fields['maintainer_email'] = email unless email.nil?
|
130
|
+
write_metadata %Q{
|
131
|
+
#{fields.map{|field,value| %Q{#{field}\t"#{value}"}}.join("\n")}
|
132
|
+
license "All rights reserved"
|
133
|
+
description "Installs/Configures example"
|
134
|
+
long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc'))
|
135
|
+
version "0.0.1"
|
136
|
+
}
|
137
|
+
end
|
138
|
+
|
139
|
+
# Create a placeholder minitest spec that would be linted due to its path
|
140
|
+
# unless an exclusion is specified.
|
141
|
+
def minitest_spec_attributes
|
142
|
+
write_file 'cookbooks/example/test/attributes/default_spec.rb', %q{
|
143
|
+
describe 'Example::Attributes::Default' do
|
144
|
+
end
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
148
|
+
# Create a Rakefile that uses the linter rake task
|
149
|
+
#
|
150
|
+
# @param [Symbol] task Type of task
|
151
|
+
# @param [Hash] options Task options
|
152
|
+
# @option options [String] :name Task name
|
153
|
+
# @option options [String] :files Files to process
|
154
|
+
# @option options [String] :options The options to set on the rake task
|
155
|
+
def rakefile(task, options)
|
156
|
+
rakefile_content = 'task :default => []'
|
157
|
+
task_def = case task
|
158
|
+
when :no_block then 'FoodCritic::Rake::LintTask.new'
|
159
|
+
else %Q{
|
160
|
+
FoodCritic::Rake::LintTask.new do |t|
|
161
|
+
#{"t.name = '#{options[:name]}'" if options[:name]}
|
162
|
+
#{"t.files = #{options[:files]}" if options[:files]}
|
163
|
+
#{"t.options = #{options[:options]}" if options[:options]}
|
164
|
+
end
|
165
|
+
}
|
166
|
+
end
|
167
|
+
if task_def
|
168
|
+
rakefile_content = %Q{
|
169
|
+
require 'foodcritic'
|
170
|
+
task :default => [:#{options[:name] ? options[:name] : 'foodcritic'}]
|
171
|
+
#{task_def}
|
172
|
+
}
|
173
|
+
end
|
174
|
+
write_file 'cookbooks/example/Rakefile', rakefile_content
|
175
|
+
end
|
176
|
+
|
177
|
+
# Create a recipe that downloads a file
|
178
|
+
#
|
179
|
+
# @param [Symbol] path_type The type of path, one of: :tmp_dir, :chef_file_cache_dir, :home_dir
|
180
|
+
def recipe_downloads_file(path_type)
|
181
|
+
download_path = {:tmp_dir => '/tmp/large-file.tar.gz',
|
182
|
+
:tmp_dir_expr => '/tmp/#{file}',
|
183
|
+
:home_dir => '/home/ernie/large-file.tar.gz',
|
184
|
+
:chef_file_cache_dir => '#{Chef::Config[:file_cache_path]}/large-file.tar.gz'}[path_type]
|
185
|
+
write_recipe %Q{
|
186
|
+
remote_file "#{download_path}" do
|
187
|
+
source "http://www.example.org/large-file.tar.gz"
|
188
|
+
end
|
189
|
+
}
|
190
|
+
end
|
191
|
+
|
192
|
+
# Install a gem using the specified approach.
|
193
|
+
#
|
194
|
+
# @param [Symbol] type The type of approach, one of :simple, :compile_time,
|
195
|
+
# :compile_time_from_array, :compile_time_from_word_list
|
196
|
+
# @param [Symbol] action Either :install or :upgrade
|
197
|
+
def recipe_installs_gem(type, action = :install)
|
198
|
+
case type
|
199
|
+
when :simple
|
200
|
+
write_recipe %Q{
|
201
|
+
gem_package "bluepill" do
|
202
|
+
action :#{action}
|
203
|
+
end
|
204
|
+
}.strip
|
205
|
+
when :compile_time
|
206
|
+
write_recipe %Q{
|
207
|
+
r = gem_package "mysql" do
|
208
|
+
action :nothing
|
209
|
+
end
|
210
|
+
|
211
|
+
r.run_action(:#{action})
|
212
|
+
Gem.clear_paths
|
213
|
+
}.strip
|
214
|
+
when :compile_time_from_array
|
215
|
+
write_recipe %Q{
|
216
|
+
['foo', 'bar', 'baz'].each do |pkg|
|
217
|
+
r = gem_package pkg do
|
218
|
+
action :nothing
|
219
|
+
end
|
220
|
+
r.run_action(:#{action})
|
221
|
+
end
|
222
|
+
}.strip
|
223
|
+
when :compile_time_from_word_list
|
224
|
+
write_recipe %Q{
|
225
|
+
%w{foo bar baz}.each do |pkg|
|
226
|
+
r = gem_package pkg do
|
227
|
+
action :nothing
|
228
|
+
end
|
229
|
+
r.run_action(:#{action})
|
230
|
+
end
|
231
|
+
}.strip
|
232
|
+
else
|
233
|
+
fail "Unrecognised type: #{type}"
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Create a recipe that declares a resource with the specified file mode.
|
238
|
+
#
|
239
|
+
# @param [String] type The type of resource (file, template)
|
240
|
+
# @param [String] mode The file mode as a string
|
241
|
+
# @param [String] comment Comment that may specify to exclude a match
|
242
|
+
def recipe_resource_with_mode(type, mode, comment='')
|
243
|
+
source_att = type == 'template' ? 'source "foo.erb"' : ''
|
244
|
+
write_recipe %Q{
|
245
|
+
#{type} "/tmp/something" do #{comment}
|
246
|
+
#{source_att}
|
247
|
+
owner "root"
|
248
|
+
group "root"
|
249
|
+
mode #{mode}
|
250
|
+
action :create
|
251
|
+
end
|
252
|
+
}
|
253
|
+
end
|
254
|
+
|
255
|
+
# Create a recipe that controls a service using the specified method.
|
256
|
+
#
|
257
|
+
# @param [Symbol] method How to start the service, one of: :init_d, :invoke_rc_d, :upstart, :service, :service_full_path.
|
258
|
+
# @param [Boolean] do_sleep Whether to prefix the service cmd with a bash sleep
|
259
|
+
# @param [Symbol] action The action to take (start, stop, reload, restart)
|
260
|
+
def recipe_controls_service(method = :service, do_sleep = false, action = :start)
|
261
|
+
cmds = {:init_d => "/etc/init.d/foo #{action}", :invoke_rc_d => "invoke-rc.d foo #{action}", :upstart => "#{action} foo",
|
262
|
+
:service => "service foo #{action}", :service_full_path => "/sbin/service foo #{action}"}
|
263
|
+
write_recipe %Q{
|
264
|
+
execute "#{action}-foo-service" do
|
265
|
+
command "#{do_sleep ? 'sleep 5; ' : ''}#{cmds[method]}"
|
266
|
+
action :run
|
267
|
+
end
|
268
|
+
}
|
269
|
+
end
|
270
|
+
|
271
|
+
# Create a recipe with an external dependency on another cookbook.
|
272
|
+
#
|
273
|
+
# @param [Hash] dep The options to use for dependency
|
274
|
+
# @option dep [Boolean] :is_declared True if this dependency has been declared in the cookbook metadata
|
275
|
+
# @option dep [Boolean] :is_scoped True if the include_recipe references a specific recipe or the cookbook
|
276
|
+
def recipe_with_dependency(dep)
|
277
|
+
dep = {:is_scoped => true, :is_declared => true}.merge!(dep)
|
278
|
+
write_recipe %Q{
|
279
|
+
include_recipe 'foo#{dep[:is_scoped] ? '::default' : ''}'
|
280
|
+
}
|
281
|
+
write_metadata %Q{
|
282
|
+
version "1.9.0"
|
283
|
+
depends "#{dep[:is_declared] ? 'foo' : 'dogs'}"
|
284
|
+
}
|
285
|
+
end
|
286
|
+
|
287
|
+
# Create a recipe with a directory resource
|
288
|
+
#
|
289
|
+
# @param [Symbol] path_expr_type The type of path expression, one of: :compound_symbols, :interpolated_string,
|
290
|
+
# :interpolated_symbol, :interpolated_symbol_and_literal, :literal_and_interpolated_symbol, :string_literal.
|
291
|
+
def recipe_with_dir_path(path_expr_type)
|
292
|
+
path = {:compound_symbols => '#{node[:base_dir]}#{node[:sub_dir]}', :interpolated_string => %q{#{node['base_dir']}},
|
293
|
+
:interpolated_symbol => '#{node[:base_dir]}', :interpolated_symbol_and_literal => '#{node[:base_dir]}/sub_dir',
|
294
|
+
:literal_and_interpolated_symbol => 'base_dir/#{node[:sub_dir]}', :string_literal => '/var/lib/foo' }[path_expr_type]
|
295
|
+
write_recipe %Q{
|
296
|
+
directory "#{path}" do
|
297
|
+
owner "root"
|
298
|
+
group "root"
|
299
|
+
mode "0755"
|
300
|
+
action :create
|
301
|
+
end
|
302
|
+
}
|
303
|
+
end
|
304
|
+
|
305
|
+
# Create a recipe with the specified resource type and attribute names.
|
306
|
+
#
|
307
|
+
# @param [String] type The type of resource
|
308
|
+
# @param [Array] attribute_names The attributes to declare on this resource
|
309
|
+
def recipe_with_resource(type, attribute_names)
|
310
|
+
write_recipe %Q{
|
311
|
+
#{type} "resource-name" do
|
312
|
+
#{attribute_names.join(" 'foo'\n")} 'bar'
|
313
|
+
end
|
314
|
+
}
|
315
|
+
end
|
316
|
+
|
317
|
+
# Create a recipe with a ruby_block resource.
|
318
|
+
#
|
319
|
+
# @param [Symbol] length A :short or :long block, or :both
|
320
|
+
def recipe_with_ruby_block(length)
|
321
|
+
recipe = ''
|
322
|
+
if length == :short || length == :both
|
323
|
+
recipe << %q{
|
324
|
+
ruby_block "subexpressions" do
|
325
|
+
block do
|
326
|
+
rc = Chef::Util::FileEdit.new("/foo/bar.conf")
|
327
|
+
rc.search_file_replace_line(/^search/, "search #{node["foo"]["bar"]} compute-1.internal")
|
328
|
+
rc.search_file_replace_line(/^domain/, "domain #{node["foo"]["bar"]}")
|
329
|
+
rc.write_file
|
330
|
+
end
|
331
|
+
action :create
|
332
|
+
end
|
333
|
+
}
|
334
|
+
end
|
335
|
+
if length == :long || length == :both
|
336
|
+
recipe << %q{
|
337
|
+
ruby_block "too_long" do
|
338
|
+
block do
|
339
|
+
begin
|
340
|
+
do_something('with argument')
|
341
|
+
do_something_else('with another argument')
|
342
|
+
foo = Foo.new('bar')
|
343
|
+
foo.activate_turbo_boost
|
344
|
+
foo.each do |thing|
|
345
|
+
case thing
|
346
|
+
when "fee"
|
347
|
+
puts 'Fee'
|
348
|
+
when "fi"
|
349
|
+
puts 'Fi'
|
350
|
+
when "fo"
|
351
|
+
puts 'Fo'
|
352
|
+
else
|
353
|
+
puts "Fum"
|
354
|
+
end
|
355
|
+
end
|
356
|
+
rescue Some::Exception
|
357
|
+
Chef::Log.warn "Problem activating the turbo boost"
|
358
|
+
end
|
359
|
+
end
|
360
|
+
action :create
|
361
|
+
end
|
362
|
+
}
|
363
|
+
end
|
364
|
+
write_recipe(recipe)
|
365
|
+
end
|
366
|
+
|
367
|
+
# Create a recipe that performs a search of the specified type.
|
368
|
+
#
|
369
|
+
# @param [Symbol] type The type of search. One of: :invalid_syntax, :valid_syntax, :with_subexpression.
|
370
|
+
def recipe_with_search(type)
|
371
|
+
search = {:invalid_syntax => 'run_list:recipe[foo::bar]', :valid_syntax => 'run_list:recipe\[foo\:\:bar\]',
|
372
|
+
:with_subexpression => %q{roles:#{node['foo']['role']}}}[type]
|
373
|
+
write_recipe %Q{
|
374
|
+
search(:node, "#{search}") do |matching_node|
|
375
|
+
puts matching_node.to_s
|
376
|
+
end
|
377
|
+
}
|
378
|
+
end
|
379
|
+
|
380
|
+
# Create a rule with the specified Chef version constraints
|
381
|
+
#
|
382
|
+
# @param [String] from_version The from version
|
383
|
+
# @param [String] to_version The to version
|
384
|
+
def rule_with_version_constraint(from_version, to_version)
|
385
|
+
constraint = if from_version && to_version
|
386
|
+
%Q{
|
387
|
+
applies_to do |version|
|
388
|
+
version >= gem_version("#{from_version}") && version <= gem_version("#{to_version}")
|
389
|
+
end
|
390
|
+
}
|
391
|
+
elsif from_version
|
392
|
+
%Q{
|
393
|
+
applies_to do |version|
|
394
|
+
version >= gem_version("#{from_version}")
|
395
|
+
end
|
396
|
+
}
|
397
|
+
elsif to_version
|
398
|
+
%Q{
|
399
|
+
applies_to do |version|
|
400
|
+
version <= gem_version("#{to_version}")
|
401
|
+
end
|
402
|
+
}
|
403
|
+
end
|
404
|
+
write_rule %Q{
|
405
|
+
rule "FCTEST001", "Test Rule" do
|
406
|
+
#{constraint}
|
407
|
+
recipe do |ast, filename|
|
408
|
+
[file_match(filename)]
|
409
|
+
end
|
410
|
+
end
|
411
|
+
}
|
412
|
+
end
|
413
|
+
|
414
|
+
# Return the provided string or nil if 'unspecified'
|
415
|
+
#
|
416
|
+
# @param [String] str The string
|
417
|
+
# @return [String] The string or nil if 'unspecified'
|
418
|
+
def nil_if_unspecified(str)
|
419
|
+
str == 'unspecified' ? nil : str
|
420
|
+
end
|
421
|
+
|
422
|
+
# Create a README with the provided content.
|
423
|
+
#
|
424
|
+
# @param [String] content The recipe content.
|
425
|
+
# @param [String] cookbook_name Optional name of the cookbook.
|
426
|
+
def write_readme(content, cookbook_name = 'example')
|
427
|
+
write_file "cookbooks/#{cookbook_name}/README.md", content.strip
|
428
|
+
end
|
429
|
+
|
430
|
+
# Create a recipe with the provided content.
|
431
|
+
#
|
432
|
+
# @param [String] content The recipe content.
|
433
|
+
# @param [String] cookbook_name Optional name of the cookbook.
|
434
|
+
def write_recipe(content, cookbook_name = 'example')
|
435
|
+
write_file "cookbooks/#{cookbook_name}/recipes/default.rb", content.strip
|
436
|
+
end
|
437
|
+
|
438
|
+
# Create a rule with the provided content.
|
439
|
+
#
|
440
|
+
# @param [String] content The rule content.
|
441
|
+
def write_rule(content)
|
442
|
+
write_file "rules/test.rb", content.strip
|
443
|
+
end
|
444
|
+
|
445
|
+
# Create attributes with the provided content.
|
446
|
+
#
|
447
|
+
# @param [String] content The attributes content.
|
448
|
+
def write_attributes(content)
|
449
|
+
write_file 'cookbooks/example/attributes/default.rb', content.strip
|
450
|
+
end
|
451
|
+
|
452
|
+
# Create a definition with the provided content.
|
453
|
+
#
|
454
|
+
# @param [String] name The definition name.
|
455
|
+
# @param [String] content The definition content.
|
456
|
+
def write_definition(name, content)
|
457
|
+
write_file "cookbooks/example/definitions/#{name}.rb", content.strip
|
458
|
+
end
|
459
|
+
|
460
|
+
# Create a library with the provided content.
|
461
|
+
#
|
462
|
+
# @param [String] name The library name.
|
463
|
+
# @param [String] content The library content.
|
464
|
+
def write_library(name, content)
|
465
|
+
write_file "cookbooks/example/libraries/#{name}.rb", content.strip
|
466
|
+
end
|
467
|
+
|
468
|
+
# Create metdata with the provided content.
|
469
|
+
#
|
470
|
+
# @param [String] content The metadata content.
|
471
|
+
def write_metadata(content)
|
472
|
+
write_file 'cookbooks/example/metadata.rb', content.strip
|
473
|
+
end
|
474
|
+
|
475
|
+
# Create a resource with the provided content.
|
476
|
+
#
|
477
|
+
# @param [String] name The resource name.
|
478
|
+
# @param [String] content The resource content.
|
479
|
+
def write_resource(name, content)
|
480
|
+
write_file "cookbooks/example/resources/#{name}.rb", content.strip
|
481
|
+
end
|
482
|
+
|
483
|
+
# Create a provider with the provided content.
|
484
|
+
#
|
485
|
+
# @param [String] name The resource name.
|
486
|
+
# @param [String] content The resource content.
|
487
|
+
def write_provider(name, content)
|
488
|
+
write_file "cookbooks/example/providers/#{name}.rb", content.strip
|
489
|
+
end
|
490
|
+
|
491
|
+
end
|
492
|
+
|
493
|
+
end
|
494
|
+
|
495
|
+
World(FoodCritic::CookbookHelpers)
|