cookbook-development 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|