cookbook-development 0.0.5
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.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/Rakefile +1 -0
- data/cookbook-development.gemspec +34 -0
- data/lib/cookbook/development.rb +7 -0
- data/lib/cookbook/development/rake/release_tasks.rb +145 -0
- data/lib/cookbook/development/rake/test_tasks.rb +99 -0
- data/lib/cookbook/development/rake/version_tasks.rb +118 -0
- data/lib/cookbook/development/rake_tasks.rb +7 -0
- data/lib/cookbook/development/test/unit/chefspec.rb +4 -0
- data/lib/cookbook/development/test/unit/chefspec/helpers.rb +1 -0
- data/lib/cookbook/development/test/unit/chefspec/helpers/stub_helpers.rb +16 -0
- data/lib/cookbook/development/test/unit/chefspec/matchers.rb +2 -0
- data/lib/cookbook/development/test/unit/chefspec/matchers/ark.rb +14 -0
- data/lib/cookbook/development/test/unit/chefspec/matchers/resource.rb +32 -0
- data/lib/cookbook/development/version.rb +5 -0
- data/lib/foodcritic/rules/etsy.rb +74 -0
- data/lib/foodcritic/rules/rally.rb +24 -0
- metadata +231 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Rally Dev Pair
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cookbook/development/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'cookbook-development'
|
8
|
+
spec.version = Cookbook::Development::VERSION
|
9
|
+
spec.authors = ['Rally Software Development Corp']
|
10
|
+
spec.email = ['rallysoftware-cookbooks@rallydev.com']
|
11
|
+
spec.description = %q{Rally Software Development Corp cookbook development}
|
12
|
+
spec.summary = %q{Rally Software Development Corp cookbook development}
|
13
|
+
spec.homepage = ''
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_dependency 'berkshelf', '~> 2.0'
|
22
|
+
spec.add_dependency 'bundler', '~> 1.3'
|
23
|
+
spec.add_dependency 'chef', '~> 11.6'
|
24
|
+
spec.add_dependency 'chefspec', '~> 3.0'
|
25
|
+
spec.add_dependency 'foodcritic', '~> 3.0'
|
26
|
+
# Workaround for json dependency for chef and berkshelf. Chef
|
27
|
+
# wants <= 1.7.7 and berkshelf >= 1.5 but bundler tries to grab 1.8.0
|
28
|
+
# which violates chef's requirement. Seems like a bundler bug.
|
29
|
+
spec.add_dependency 'json', '1.7.7'
|
30
|
+
spec.add_dependency 'kitchen-vagrant', '~> 0.11'
|
31
|
+
spec.add_dependency 'rake', '~> 10.0'
|
32
|
+
spec.add_dependency 'test-kitchen'
|
33
|
+
spec.add_dependency 'version', '~> 1.0'
|
34
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# Ideas and code for releasing a cookbook heavily borrowed from bundler https://github.com/bundler/bundler
|
2
|
+
module CookbookDevelopment
|
3
|
+
class ReleaseTasks < Rake::TaskLib
|
4
|
+
attr_reader :project_dir
|
5
|
+
attr_reader :chef_dir
|
6
|
+
attr_reader :knife_cfg
|
7
|
+
attr_reader :vendor_dir
|
8
|
+
attr_reader :cookbooks_dir
|
9
|
+
attr_reader :berks_file
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@project_dir = Dir.pwd
|
13
|
+
@chef_dir = File.join(project_dir, 'test', '.chef')
|
14
|
+
@knife_cfg = File.join(chef_dir, 'knife.rb')
|
15
|
+
@vendor_dir = File.join(project_dir, 'vendor')
|
16
|
+
@cookbooks_dir = File.join(vendor_dir, 'cookbooks')
|
17
|
+
@berks_file = File.join(project_dir, 'Berksfile')
|
18
|
+
|
19
|
+
yield(self) if block_given?
|
20
|
+
define
|
21
|
+
end
|
22
|
+
|
23
|
+
def define
|
24
|
+
desc 'Does a berks upload --except :test'
|
25
|
+
task :upload do |task|
|
26
|
+
Berkshelf::Berksfile.from_file(berks_file).upload(:path => cookbooks_dir, :except => :test)
|
27
|
+
end
|
28
|
+
|
29
|
+
desc 'Runs the full test suite and then does a berks upload'
|
30
|
+
task :release do
|
31
|
+
release_cookbook
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def release_cookbook(skip_test = false)
|
37
|
+
start_time = Time.now
|
38
|
+
release_version = version
|
39
|
+
release_tag = version_tag(release_version)
|
40
|
+
|
41
|
+
raise "Tag #{release_tag} has already been created." if already_tagged?(release_tag)
|
42
|
+
raise 'You have uncommitted changes.' unless clean? && committed?
|
43
|
+
raise 'You have unpushed commits.' if unpushed?
|
44
|
+
|
45
|
+
Rake::Task[:test].invoke unless ENV['test'] == 'false'
|
46
|
+
|
47
|
+
tag_version(release_version, release_tag) do
|
48
|
+
berks_upload
|
49
|
+
Rake::Task['version:bump:patch'].invoke
|
50
|
+
git_pull
|
51
|
+
git_push
|
52
|
+
end
|
53
|
+
|
54
|
+
elapsed = Time.now - start_time
|
55
|
+
puts elapsed
|
56
|
+
end
|
57
|
+
|
58
|
+
def berks_upload
|
59
|
+
puts 'Running berks upload...'
|
60
|
+
Rake::Task[:upload].invoke
|
61
|
+
end
|
62
|
+
|
63
|
+
def git_pull
|
64
|
+
cmd = 'git pull --rebase'
|
65
|
+
out, code = sh_with_code(cmd)
|
66
|
+
raise "Couldn't git pull. `#{cmd}' failed with the following output:\n\n#{out}\n" unless code == 0
|
67
|
+
end
|
68
|
+
|
69
|
+
def git_push
|
70
|
+
puts 'Pushing git changes...'
|
71
|
+
perform_git_push 'origin --tags :'
|
72
|
+
puts 'Pushed git commits and tags.'
|
73
|
+
end
|
74
|
+
|
75
|
+
def perform_git_push(options = '')
|
76
|
+
cmd = "git push #{options}"
|
77
|
+
out, code = sh_with_code(cmd)
|
78
|
+
raise "Couldn't git push. `#{cmd}' failed with the following output:\n\n#{out}\n" unless code == 0
|
79
|
+
end
|
80
|
+
|
81
|
+
def clean?
|
82
|
+
sh_with_code('git diff --exit-code')[1] == 0
|
83
|
+
end
|
84
|
+
|
85
|
+
def committed?
|
86
|
+
sh_with_code('git diff-index --quiet --cached HEAD')[1] == 0
|
87
|
+
end
|
88
|
+
|
89
|
+
def unpushed?
|
90
|
+
sh_with_code('git cherry')[0] != ''
|
91
|
+
end
|
92
|
+
|
93
|
+
def sh(cmd, &block)
|
94
|
+
out, code = sh_with_code(cmd, &block)
|
95
|
+
code == 0 ? out : raise(out.empty? ? "Running `#{cmd}' failed. Run this command directly for more detailed output." : out)
|
96
|
+
end
|
97
|
+
|
98
|
+
def already_tagged?(tag)
|
99
|
+
sh('git tag').split(/\n/).include?(tag)
|
100
|
+
end
|
101
|
+
|
102
|
+
def version_tag(version)
|
103
|
+
"v#{version}"
|
104
|
+
end
|
105
|
+
|
106
|
+
def version
|
107
|
+
version_file = VersionFile::VERSION_FILE
|
108
|
+
alt_version_file = VersionFile::ALT_VERSION_FILE
|
109
|
+
|
110
|
+
if File.exist?(version_file)
|
111
|
+
Version.current(version_file)
|
112
|
+
elsif File.exist?(alt_version_file)
|
113
|
+
Version.current(alt_version_file)
|
114
|
+
else
|
115
|
+
raise <<-MSG
|
116
|
+
The versioning/release process relies on having a VERSION file in the root of
|
117
|
+
your cookbook as well as the version attribute in metadata.rb reading
|
118
|
+
from said VERSION file. Until https://github.com/opscode/test-kitchen/pull/212
|
119
|
+
is resolved we need to put the cookbooks in a place that test-kitchen
|
120
|
+
will copy to the VM. That place is in recipes/VERSION. Neither #{version_file}
|
121
|
+
nor #{alt_version_file} were found in your cookbook.
|
122
|
+
MSG
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def tag_version(release, tag)
|
127
|
+
sh "git tag -a -m \"Version #{release}\" #{tag}"
|
128
|
+
puts "Tagged #{tag}."
|
129
|
+
yield if block_given?
|
130
|
+
rescue Exception => e
|
131
|
+
puts "Untagging #{tag} due to error."
|
132
|
+
sh_with_code "git tag -d #{tag}"
|
133
|
+
raise e
|
134
|
+
end
|
135
|
+
|
136
|
+
def sh_with_code(cmd, &block)
|
137
|
+
cmd << " 2>&1"
|
138
|
+
outbuf = `#{cmd}`
|
139
|
+
if $? == 0
|
140
|
+
block.call(outbuf) if block
|
141
|
+
end
|
142
|
+
[outbuf, $?]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'rspec/core/rake_task'
|
2
|
+
require 'kitchen/rake_tasks'
|
3
|
+
require 'foodcritic'
|
4
|
+
require 'berkshelf'
|
5
|
+
|
6
|
+
module CookbookDevelopment
|
7
|
+
class TestTasks < Rake::TaskLib
|
8
|
+
attr_reader :project_dir
|
9
|
+
attr_reader :chef_dir
|
10
|
+
attr_reader :knife_cfg
|
11
|
+
attr_reader :vendor_dir
|
12
|
+
attr_reader :cookbooks_dir
|
13
|
+
attr_reader :berks_file
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@project_dir = Dir.pwd
|
17
|
+
@chef_dir = File.join(project_dir, 'test', '.chef')
|
18
|
+
@knife_cfg = File.join(chef_dir, 'knife.rb')
|
19
|
+
@vendor_dir = File.join(project_dir, 'vendor')
|
20
|
+
@cookbooks_dir = File.join(vendor_dir, 'cookbooks')
|
21
|
+
@berks_file = File.join(project_dir, 'Berksfile')
|
22
|
+
@metadata_file = File.join(project_dir, 'metadata.rb')
|
23
|
+
|
24
|
+
yield(self) if block_given?
|
25
|
+
define
|
26
|
+
end
|
27
|
+
|
28
|
+
def define
|
29
|
+
|
30
|
+
kitchen_config = Kitchen::Config.new
|
31
|
+
Kitchen.logger = Kitchen.default_file_logger
|
32
|
+
|
33
|
+
namespace "kitchen" do
|
34
|
+
kitchen_config.instances.each do |instance|
|
35
|
+
desc "Run #{instance.name} test instance"
|
36
|
+
task instance.name do
|
37
|
+
instance.test(:passing)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Run all test instances"
|
42
|
+
task "all" => kitchen_config.instances.map { |i| i.name }
|
43
|
+
end
|
44
|
+
|
45
|
+
desc 'Runs knife cookbook test'
|
46
|
+
task :knife_test => [knife_cfg, :berks_install] do |task|
|
47
|
+
cookbook_name = File.basename(project_dir)
|
48
|
+
IO.readlines(@metadata_file).each do |line|
|
49
|
+
cookbook_name = line.split[1] if line.split[0] == 'name'
|
50
|
+
end
|
51
|
+
Dir.chdir(File.join(project_dir, '..')) do
|
52
|
+
sh "bundle exec knife cookbook test #{cookbook_name} --config #{knife_cfg}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
desc 'Runs Foodcritic linting'
|
57
|
+
FoodCritic::Rake::LintTask.new do |task|
|
58
|
+
task.options = {:search_gems => true, :fail_tags => ['any'], :tags => ['~FC003', '~FC015'], :exclude_paths => ['vendor/cookbooks/**/*']}
|
59
|
+
end
|
60
|
+
|
61
|
+
desc 'Runs unit tests'
|
62
|
+
RSpec::Core::RakeTask.new(:unit) do |task|
|
63
|
+
task.pattern = FileList[File.join(project_dir, 'test', 'unit', '**/*_spec.rb')]
|
64
|
+
end
|
65
|
+
task :unit => :berks_install
|
66
|
+
|
67
|
+
desc 'Runs integration tests'
|
68
|
+
task :integration do
|
69
|
+
Rake::Task['kitchen:all'].invoke
|
70
|
+
end
|
71
|
+
|
72
|
+
desc 'Run all tests and linting'
|
73
|
+
task :test do
|
74
|
+
Rake::Task['knife_test'].invoke
|
75
|
+
Rake::Task['foodcritic'].invoke
|
76
|
+
Rake::Task['unit'].invoke
|
77
|
+
Rake::Task['integration'].invoke
|
78
|
+
end
|
79
|
+
|
80
|
+
task :berks_install do |task|
|
81
|
+
FileUtils.rm_rf vendor_dir
|
82
|
+
Berkshelf::Berksfile.from_file(berks_file).install(:path => cookbooks_dir)
|
83
|
+
end
|
84
|
+
|
85
|
+
directory chef_dir
|
86
|
+
|
87
|
+
file knife_cfg => chef_dir do |task|
|
88
|
+
File.open(task.name, 'w+') do |file|
|
89
|
+
file.write <<-EOF.gsub(/^\s+/, "")
|
90
|
+
cache_type 'BasicFile'
|
91
|
+
cache_options(:path => "\#{ENV['HOME']}/.chef/checksums")
|
92
|
+
cookbook_path '#{cookbooks_dir}'
|
93
|
+
EOF
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'version'
|
2
|
+
require 'rake/tasklib'
|
3
|
+
|
4
|
+
module CookbookDevelopment
|
5
|
+
|
6
|
+
class VersionFile
|
7
|
+
VERSION_FILE = File.join(Dir.pwd, 'VERSION')
|
8
|
+
ALT_VERSION_FILE = File.join(Dir.pwd, 'recipes', 'VERSION')
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def in_dir(dir = Dir.pwd)
|
12
|
+
metadata_file = File.join(Dir.pwd, 'metadata.rb')
|
13
|
+
|
14
|
+
if File.exist? VERSION_FILE
|
15
|
+
VersionFile.new(VERSION_FILE)
|
16
|
+
elsif File.exist? ALT_VERSION_FILE
|
17
|
+
# The release process relies on having a VERSION file in the root of
|
18
|
+
# your cookbook as well as the version attribute in metadata.rb reading
|
19
|
+
# from said VERSION file. Until https://github.com/opscode/test-kitchen/pull/212
|
20
|
+
# is resolved we need to put the cookbooks in a place that test-kitchen
|
21
|
+
# will copy to the VM.
|
22
|
+
VersionFile.new(ALT_VERSION_FILE)
|
23
|
+
elsif File.exist? metadata_file
|
24
|
+
MetadataVersion.new(metadata_file)
|
25
|
+
else
|
26
|
+
raise 'I could not find a VERSION file or a metadata.rb'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_reader :version
|
32
|
+
attr_reader :path
|
33
|
+
def initialize(path)
|
34
|
+
@path = Pathname.new(path)
|
35
|
+
@version = @path.read
|
36
|
+
end
|
37
|
+
|
38
|
+
def bump(level)
|
39
|
+
@version.to_version.bump!(level).to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def bump!(level)
|
43
|
+
@version = bump(level)
|
44
|
+
save
|
45
|
+
@version
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
@version
|
50
|
+
end
|
51
|
+
|
52
|
+
def save
|
53
|
+
@path.open('w') do |io|
|
54
|
+
io << self
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class MetadataVersion < VersionFile
|
60
|
+
def initialize(path)
|
61
|
+
@path = Pathname.new(path)
|
62
|
+
@metadata = @path.read
|
63
|
+
@metadata =~ /version\s+'(\d+\.\d+\.\d+)'/
|
64
|
+
@version = $1
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_s
|
68
|
+
@metadata.sub(/(^version\s+')(\d+\.\d+\.\d+)(')/, "\\1#{@version}\\3" )
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class VersionTasks < Rake::TaskLib
|
73
|
+
def initialize
|
74
|
+
@version_file = VersionFile.in_dir(Dir.pwd)
|
75
|
+
yield(self) if block_given?
|
76
|
+
define
|
77
|
+
end
|
78
|
+
|
79
|
+
def define
|
80
|
+
namespace :version do
|
81
|
+
namespace :bump do
|
82
|
+
desc "Bump to #{@version_file.bump(:major)}"
|
83
|
+
task :major do
|
84
|
+
bump_version!(:major)
|
85
|
+
end
|
86
|
+
|
87
|
+
desc "Bump to #{@version_file.bump(:minor)}"
|
88
|
+
task :minor do
|
89
|
+
bump_version!(:minor)
|
90
|
+
end
|
91
|
+
|
92
|
+
desc "Bump to #{@version_file.bump(:revision)}"
|
93
|
+
task :patch do
|
94
|
+
bump_version!(:revision)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def bump_version!(level)
|
103
|
+
new_version = @version_file.bump!(level)
|
104
|
+
puts "Version bumped to #{new_version}"
|
105
|
+
git_commit(new_version)
|
106
|
+
end
|
107
|
+
|
108
|
+
def system(*args)
|
109
|
+
abort unless Kernel.system(*args)
|
110
|
+
end
|
111
|
+
|
112
|
+
def git_commit(version)
|
113
|
+
puts "Committing #{@version_file.path}..."
|
114
|
+
system("git add #{@version_file.path}")
|
115
|
+
system("git commit -m 'Version bump to #{version}'")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
require 'cookbook/development/rake/version_tasks'
|
2
|
+
require 'cookbook/development/rake/test_tasks'
|
3
|
+
require 'cookbook/development/rake/release_tasks'
|
4
|
+
|
5
|
+
CookbookDevelopment::TestTasks.new
|
6
|
+
CookbookDevelopment::VersionTasks.new
|
7
|
+
CookbookDevelopment::ReleaseTasks.new
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'cookbook/development/test/unit/chefspec/helpers/stub_helpers'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module StubHelpers
|
2
|
+
|
3
|
+
def stub_include_recipe
|
4
|
+
# Don't worry about external cookbook dependencies
|
5
|
+
Chef::Cookbook::Metadata.any_instance.stub(:depends)
|
6
|
+
|
7
|
+
# Test each recipe in isolation, regardless of includes
|
8
|
+
@included_recipes = []
|
9
|
+
Chef::RunContext.any_instance.stub(:loaded_recipe?).and_return(false)
|
10
|
+
Chef::Recipe.any_instance.stub(:include_recipe) do |i|
|
11
|
+
Chef::RunContext.any_instance.stub(:loaded_recipe?).with(i).and_return(true)
|
12
|
+
@included_recipes << i
|
13
|
+
end
|
14
|
+
Chef::RunContext.any_instance.stub(:loaded_recipes).and_return(@included_recipes)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ChefSpec::API
|
2
|
+
|
3
|
+
module ArkMatchers
|
4
|
+
|
5
|
+
def put_ark(resource_name)
|
6
|
+
ChefSpec::Matchers::ResourceMatcher.new(:ark, :put, resource_name)
|
7
|
+
end
|
8
|
+
|
9
|
+
def install_ark(resource_name)
|
10
|
+
ChefSpec::Matchers::ResourceMatcher.new(:ark, :install, resource_name)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ChefSpec
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
RSpec::Matchers.define :ready_resource_with_attribute do |resource_type, resource_name, attributes|
|
5
|
+
match do |chef_run|
|
6
|
+
found_resource = chef_run.find_resource(resource_type, resource_name)
|
7
|
+
found_resource.nil? == false && \
|
8
|
+
found_resource.action == [:nothing] && \
|
9
|
+
attributes.any? { |attribute, value| found_resource.send(attribute) == value }
|
10
|
+
end
|
11
|
+
|
12
|
+
failure_message_for_should do |chef_run|
|
13
|
+
"Expected #{resource_type}[#{resource_name}] with action [:nothing] && #{attributes} to be in Chef run. Other #{resource_type} resources:" \
|
14
|
+
"\n\n " + inspect_other_resources(chef_run, resource_type, attributes).join("\n ") + "\n "
|
15
|
+
end
|
16
|
+
|
17
|
+
failure_message_for_should_not do |chef_run|
|
18
|
+
"Should not have found execute[#{resource_name}] with action [:nothing] && #{attributes}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect_other_resources(chef_run, resource_type, attributes)
|
22
|
+
parameter, param_value = nil, nil
|
23
|
+
attributes.each_pair { |key, value| parameter, param_value = key, value }
|
24
|
+
resources = []
|
25
|
+
chef_run.find_resources(resource_type).each do |resource|
|
26
|
+
resources << "#{resource_type}[#{resource.name}] has:\n\t\taction: #{resource.action.inspect}\n\t\t#{parameter}: `#{resource.send(parameter).to_s}`"
|
27
|
+
end
|
28
|
+
resources
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
#Etsy Foodcritic rules
|
2
|
+
@coreservices = ["httpd", "mysql", "memcached", "postgresql-server"]
|
3
|
+
@coreservicepackages = ["httpd", "Percona-Server-server-51", "memcached", "postgresql-server"]
|
4
|
+
@corecommands = ["yum -y", "yum install", "yum reinstall", "yum remove", "mkdir", "useradd", "usermod", "touch"]
|
5
|
+
|
6
|
+
rule "ETSY001", "Package or yum_package resource used with :upgrade action" do
|
7
|
+
tags %w{correctness recipe etsy}
|
8
|
+
recipe do |ast|
|
9
|
+
pres = find_resources(ast, :type => 'package').find_all do |cmd|
|
10
|
+
cmd_str = (resource_attribute(cmd, 'action') || resource_name(cmd)).to_s
|
11
|
+
cmd_str.include?('upgrade')
|
12
|
+
end
|
13
|
+
ypres = find_resources(ast, :type => 'yum_package').find_all do |cmd|
|
14
|
+
cmd_str = (resource_attribute(cmd, 'action') || resource_name(cmd)).to_s
|
15
|
+
cmd_str.include?('upgrade')
|
16
|
+
end
|
17
|
+
pres.concat(ypres).map{|cmd| match(cmd)}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
#ETSY002 and ETSY003 removed as they were added to mainline foodcritic as FC040 and FC041
|
22
|
+
|
23
|
+
# This rule does not detect execute resources defined inside a conditional, as foodcritic rule FC023 (Prefer conditional attributes)
|
24
|
+
# already provides this. It's recommended to use both rules in conjunction. (foodcritic -t etsy,FC023)
|
25
|
+
rule "ETSY004", "Execute resource defined without conditional or action :nothing" do
|
26
|
+
tags %w{style recipe etsy}
|
27
|
+
recipe do |ast,filename|
|
28
|
+
pres = find_resources(ast, :type => 'execute').find_all do |cmd|
|
29
|
+
cmd_actions = (resource_attribute(cmd, 'action') || resource_name(cmd)).to_s
|
30
|
+
condition = cmd.xpath('//ident[@value="only_if" or @value="not_if" or @value="creates"][parent::fcall or parent::command or ancestor::if]')
|
31
|
+
(condition.empty? && !cmd_actions.include?("nothing"))
|
32
|
+
end.map{|cmd| match(cmd)}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
rule "ETSY005", "Action :restart sent to a core service" do
|
37
|
+
tags %w{style recipe etsy}
|
38
|
+
recipe do |ast, filename|
|
39
|
+
find_resources(ast).select do |resource|
|
40
|
+
notifications(resource).any? do |notification|
|
41
|
+
@coreservices.include?(notification[:resource_name]) and
|
42
|
+
notification[:action] == :restart
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
rule "ETSY006", "Execute resource used to run chef-provided command" do
|
49
|
+
tags %w{style recipe etsy}
|
50
|
+
recipe do |ast|
|
51
|
+
find_resources(ast, :type => 'execute').find_all do |cmd|
|
52
|
+
cmd_str = (resource_attribute(cmd, 'command') || resource_name(cmd)).to_s
|
53
|
+
@corecommands.any? { |corecommand| cmd_str.include? corecommand }
|
54
|
+
end.map{|c| match(c)}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
rule "ETSY007", "Package or yum_package resource used to install core package without specific version number" do
|
60
|
+
tags %w{style recipe etsy}
|
61
|
+
recipe do |ast,filename|
|
62
|
+
pres = find_resources(ast, :type => 'package').find_all do |cmd|
|
63
|
+
cmd_str = (resource_attribute(cmd, 'version') || resource_name(cmd)).to_s
|
64
|
+
cmd_action = (resource_attribute(cmd, 'action') || resource_name(cmd)).to_s
|
65
|
+
cmd_str == resource_name(cmd) && @coreservicepackages.any? { |svc| resource_name(cmd) == svc } && cmd_action.include?('install')
|
66
|
+
end
|
67
|
+
ypres = find_resources(ast, :type => 'yum_package').find_all do |cmd|
|
68
|
+
cmd_str = (resource_attribute(cmd, 'version') || resource_name(cmd)).to_s
|
69
|
+
cmd_action = (resource_attribute(cmd, 'action') || resource_name(cmd)).to_s
|
70
|
+
cmd_str == resource_name(cmd) && @coreservicepackages.any? { |svc| resource_name(cmd) == svc } && cmd_action.include?('install')
|
71
|
+
end
|
72
|
+
pres.concat(ypres).map{|cmd| match(cmd)}
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Borrowed from customink's foodcritic rules
|
2
|
+
# See https://github.com/customink-webops/foodcritic-rules/blob/master/rules.rb
|
3
|
+
|
4
|
+
rule 'RALY001', 'Prefer single-quoted strings' do
|
5
|
+
tags %w{style strings}
|
6
|
+
recipe do |ast, filename|
|
7
|
+
|
8
|
+
next if filename.end_with? '.erb'
|
9
|
+
|
10
|
+
lines = File.readlines(filename)
|
11
|
+
|
12
|
+
lines.collect.with_index do |line, index|
|
13
|
+
# Don't flag if there is a #{} or ' in the line
|
14
|
+
if line.match('"(.*)"') && !line.match('^\s+<.+?[class|plugin]="(.+?)".*?>\s*$') && !line.match('\A\s?#') && !line.match('\'(.*)"(.*)"(.*)\'') && !line.match('"(.*)(#{.+}|\'|\\\a|\\\b|\\\r|\\\n|\\\s|\\\t)(.*)"')
|
15
|
+
{
|
16
|
+
:filename => filename,
|
17
|
+
:matched => recipe,
|
18
|
+
:line => index + 1,
|
19
|
+
:column => 0
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end.compact.flatten
|
23
|
+
end
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,231 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cookbook-development
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.5
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Rally Software Development Corp
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-12-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: berkshelf
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: bundler
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.3'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.3'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: chef
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '11.6'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '11.6'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: chefspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '3.0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '3.0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: foodcritic
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '3.0'
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '3.0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: json
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - '='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 1.7.7
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - '='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 1.7.7
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: kitchen-vagrant
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.11'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0.11'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: rake
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ~>
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '10.0'
|
134
|
+
type: :runtime
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ~>
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '10.0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: test-kitchen
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :runtime
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
name: version
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ~>
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '1.0'
|
166
|
+
type: :runtime
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ~>
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '1.0'
|
174
|
+
description: Rally Software Development Corp cookbook development
|
175
|
+
email:
|
176
|
+
- rallysoftware-cookbooks@rallydev.com
|
177
|
+
executables: []
|
178
|
+
extensions: []
|
179
|
+
extra_rdoc_files: []
|
180
|
+
files:
|
181
|
+
- .gitignore
|
182
|
+
- Gemfile
|
183
|
+
- LICENSE.txt
|
184
|
+
- Rakefile
|
185
|
+
- cookbook-development.gemspec
|
186
|
+
- lib/cookbook/development.rb
|
187
|
+
- lib/cookbook/development/rake/release_tasks.rb
|
188
|
+
- lib/cookbook/development/rake/test_tasks.rb
|
189
|
+
- lib/cookbook/development/rake/version_tasks.rb
|
190
|
+
- lib/cookbook/development/rake_tasks.rb
|
191
|
+
- lib/cookbook/development/test/unit/chefspec.rb
|
192
|
+
- lib/cookbook/development/test/unit/chefspec/helpers.rb
|
193
|
+
- lib/cookbook/development/test/unit/chefspec/helpers/stub_helpers.rb
|
194
|
+
- lib/cookbook/development/test/unit/chefspec/matchers.rb
|
195
|
+
- lib/cookbook/development/test/unit/chefspec/matchers/ark.rb
|
196
|
+
- lib/cookbook/development/test/unit/chefspec/matchers/resource.rb
|
197
|
+
- lib/cookbook/development/version.rb
|
198
|
+
- lib/foodcritic/rules/etsy.rb
|
199
|
+
- lib/foodcritic/rules/rally.rb
|
200
|
+
homepage: ''
|
201
|
+
licenses:
|
202
|
+
- MIT
|
203
|
+
post_install_message:
|
204
|
+
rdoc_options: []
|
205
|
+
require_paths:
|
206
|
+
- lib
|
207
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
208
|
+
none: false
|
209
|
+
requirements:
|
210
|
+
- - ! '>='
|
211
|
+
- !ruby/object:Gem::Version
|
212
|
+
version: '0'
|
213
|
+
segments:
|
214
|
+
- 0
|
215
|
+
hash: -2590675334605747241
|
216
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
217
|
+
none: false
|
218
|
+
requirements:
|
219
|
+
- - ! '>='
|
220
|
+
- !ruby/object:Gem::Version
|
221
|
+
version: '0'
|
222
|
+
segments:
|
223
|
+
- 0
|
224
|
+
hash: -2590675334605747241
|
225
|
+
requirements: []
|
226
|
+
rubyforge_project:
|
227
|
+
rubygems_version: 1.8.25
|
228
|
+
signing_key:
|
229
|
+
specification_version: 3
|
230
|
+
summary: Rally Software Development Corp cookbook development
|
231
|
+
test_files: []
|