strainer 0.0.2
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/.rvmrc +1 -0
- data/Gemfile +3 -0
- data/LICENSE +13 -0
- data/README.md +39 -0
- data/Rakefile +2 -0
- data/bin/strain +12 -0
- data/lib/strainer.rb +9 -0
- data/lib/strainer/cli.rb +8 -0
- data/lib/strainer/color.rb +7 -0
- data/lib/strainer/runner.rb +91 -0
- data/lib/strainer/sandbox.rb +68 -0
- data/strainer.gemspec +18 -0
- metadata +93 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.3@strainer --create
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2012 CustomInk
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# Strainer
|
2
|
+
|
3
|
+
Strainer is a gem for isolating and testing individual chef cookbooks. It allows you to keep all your cookbooks in a single repository (saving you time and money), while having all the benefits of individual repositories.
|
4
|
+
|
5
|
+
Usage
|
6
|
+
-----
|
7
|
+
Strainer is a command line tool. The first thing you should do is add the following entry into your `.gitignore` and `chefignore` files:
|
8
|
+
|
9
|
+
colander
|
10
|
+
|
11
|
+
The `colander` is where strainer puts all your temporary files for testing.
|
12
|
+
|
13
|
+
Next, create a `Colanderfile`. Cookbook-level Colanderfiles will take precedence over root-level ones. For simplicity, let's just create one in the root of our chef repository:
|
14
|
+
|
15
|
+
# Colanderfile
|
16
|
+
knife test: bundle exec knife cookbook test $COOKBOOK
|
17
|
+
foodcritic: bundle exec foodcritic -f any $COOKBOOK
|
18
|
+
|
19
|
+
`Colanderfile` exposes two variables:
|
20
|
+
|
21
|
+
- `$COOKBOOK` - the current running cookbook
|
22
|
+
- `$SANDBOX` - the sandbox path
|
23
|
+
|
24
|
+
Just like foreman, the labels don't actually matter - they are only used in formatting the output.
|
25
|
+
|
26
|
+
That `Colanderfile` will run [foodcritic](https://github.com/acrmp/foodcritic) and knife test. I recommend this as the bare minimum for a cookbook test.
|
27
|
+
|
28
|
+
To strain, simply run the `strain` command and pass in the cookbooks to strain:
|
29
|
+
|
30
|
+
# strain phantomjs and tmux
|
31
|
+
bundle exec strain phantomjs tmux
|
32
|
+
|
33
|
+
This will run `knife test` and `foodcritic` against both of the cookbooks. You can pass in as many cookbooks are you'd like.
|
34
|
+
|
35
|
+
Needs Your Help
|
36
|
+
---------------
|
37
|
+
This is a list of features or problem *you* can help solve! Fork and submit a pull request to make Strain even better!
|
38
|
+
|
39
|
+
- **Threading** - Run each cookbook's tests (or each cookbook tests test) in a separate thread
|
data/Rakefile
ADDED
data/bin/strain
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'strainer'
|
7
|
+
require 'strainer/cli'
|
8
|
+
|
9
|
+
args = ARGV.dup
|
10
|
+
ARGV.clear
|
11
|
+
|
12
|
+
Strainer::CLI.run(args)
|
data/lib/strainer.rb
ADDED
data/lib/strainer/cli.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
module Strainer
|
2
|
+
class Runner
|
3
|
+
def initialize(sandbox, options = {})
|
4
|
+
@sandbox = sandbox
|
5
|
+
@cookbooks = @sandbox.cookbooks
|
6
|
+
|
7
|
+
@results = Hash.new(0)
|
8
|
+
|
9
|
+
@cookbooks.each do |cookbook|
|
10
|
+
puts Color.negative{ "# Straining '#{cookbook}'" }
|
11
|
+
commands_for(cookbook).collect do |command|
|
12
|
+
run(command)
|
13
|
+
end
|
14
|
+
puts "\n\n\n"
|
15
|
+
end
|
16
|
+
|
17
|
+
abort unless @results[:failed].zero?
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def commands_for(cookbook)
|
22
|
+
file = File.read( colanderfile_for(cookbook) )
|
23
|
+
|
24
|
+
file = file.strip
|
25
|
+
file = file.gsub('$COOKBOOK', cookbook)
|
26
|
+
file = file.gsub('$SANDBOX', @sandbox.sandbox_path)
|
27
|
+
|
28
|
+
lines = file.split("\n").reject{|c| c.strip.empty?}.compact
|
29
|
+
|
30
|
+
# parse the line and split it into the label and command parts
|
31
|
+
#
|
32
|
+
# example line: foodcritic -f any phantomjs
|
33
|
+
lines.collect do |line|
|
34
|
+
split_line = line.split(':', 2)
|
35
|
+
|
36
|
+
{
|
37
|
+
:label => split_line[0].strip,
|
38
|
+
:command => split_line[1].strip
|
39
|
+
}
|
40
|
+
end || []
|
41
|
+
end
|
42
|
+
|
43
|
+
def colanderfile_for(cookbook)
|
44
|
+
cookbook_level = File.join(@sandbox.sandbox_path(cookbook), 'Colanderfile')
|
45
|
+
root_level = File.expand_path('Colanderfile')
|
46
|
+
|
47
|
+
if File.exists?(cookbook_level)
|
48
|
+
cookbook_level
|
49
|
+
elsif File.exists?(root_level)
|
50
|
+
root_level
|
51
|
+
else
|
52
|
+
raise "Could not find Colanderfile in #{cookbook_level} or #{root_level}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def label_with_padding(label)
|
57
|
+
max_length = 12
|
58
|
+
colors = [ :blue, :cyan, :magenta, :yellow ]
|
59
|
+
color = colors[label.length%colors.length]
|
60
|
+
|
61
|
+
Color.send(color) do
|
62
|
+
"#{label[0...max_length].ljust(max_length)} | "
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def run(command)
|
67
|
+
label = command[:label]
|
68
|
+
command = command[:command]
|
69
|
+
|
70
|
+
puts [ label_with_padding(label), Color.bold{ Color.underscore{ command } } ].join(' ')
|
71
|
+
output(label, `cd #{@sandbox.sandbox_path} && #{command}`)
|
72
|
+
|
73
|
+
if $?.success?
|
74
|
+
@results[:passed] += 1
|
75
|
+
output(label, Color.green{'Success!'})
|
76
|
+
return true
|
77
|
+
else
|
78
|
+
@results[:failed] += 1
|
79
|
+
output(label, Color.red{'Failure!'})
|
80
|
+
return false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def output(label, data)
|
85
|
+
data.to_s.strip.split("\n").each do |line|
|
86
|
+
$stdout.puts [ label_with_padding(label), line ].join(' ')
|
87
|
+
$stdout.flush
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Strainer
|
4
|
+
class Sandbox
|
5
|
+
attr_reader :cookbooks
|
6
|
+
|
7
|
+
def initialize(cookbooks = [], options = {})
|
8
|
+
@cookbooks = [cookbooks].flatten
|
9
|
+
@options = options
|
10
|
+
|
11
|
+
clear_sandbox
|
12
|
+
create_sandbox
|
13
|
+
end
|
14
|
+
|
15
|
+
def cookbook_path(cookbook)
|
16
|
+
path = File.join(cookbooks_path, cookbook)
|
17
|
+
raise "cookbook '#{cookbook}' was not found in #{cookbooks_path}" unless File.exists?(path)
|
18
|
+
return path
|
19
|
+
end
|
20
|
+
|
21
|
+
def sandbox_path(cookbook = nil)
|
22
|
+
File.expand_path( File.join(%W(colander cookbooks #{cookbook})) )
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def cookbooks_path
|
27
|
+
@cookbooks_path ||= (@options[:cookbooks_path] || File.expand_path('cookbooks'))
|
28
|
+
end
|
29
|
+
|
30
|
+
def clear_sandbox
|
31
|
+
FileUtils.rm_rf(sandbox_path)
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_sandbox
|
35
|
+
FileUtils.mkdir_p(sandbox_path)
|
36
|
+
|
37
|
+
copy_globals
|
38
|
+
copy_cookbooks
|
39
|
+
place_knife_rb
|
40
|
+
end
|
41
|
+
|
42
|
+
def copy_globals
|
43
|
+
files = %w(.rspec spec test)
|
44
|
+
FileUtils.cp_r( Dir["{#{files.join(',')}}"], sandbox_path('..') )
|
45
|
+
end
|
46
|
+
|
47
|
+
def copy_cookbooks
|
48
|
+
@cookbooks.each do |cookbook|
|
49
|
+
FileUtils.cp_r(cookbook_path(cookbook), sandbox_path)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def place_knife_rb
|
54
|
+
chef_path = File.join(sandbox_path, '..','.chef')
|
55
|
+
FileUtils.mkdir_p(chef_path)
|
56
|
+
|
57
|
+
# build the contents
|
58
|
+
contents = <<-EOH
|
59
|
+
cache_type 'BasicFile'
|
60
|
+
cache_options(:path => "\#{ENV['HOME']}/.chef/checksums")
|
61
|
+
cookbook_path '#{sandbox_path}'
|
62
|
+
EOH
|
63
|
+
|
64
|
+
# create knife.rb
|
65
|
+
File.open("#{chef_path}/knife.rb", 'w+'){ |f| f.write(contents) }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/strainer.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Gem::Specification.new do |gem|
|
2
|
+
gem.version = '0.0.2'
|
3
|
+
gem.authors = ['Seth Vargo']
|
4
|
+
gem.email = ['sethvargo@gmail.com']
|
5
|
+
gem.description = %q{Run isolated cookbook tests against your chef repository with Strainer.}
|
6
|
+
gem.summary = %q{Strainer allows you to run cookbook tests in an isolated environment while still keeping a single Gemfile and repository for all your cookbooks.}
|
7
|
+
gem.homepage = 'https://github.com/customink/strainer'
|
8
|
+
|
9
|
+
gem.files = `git ls-files`.split($\)
|
10
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
11
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
12
|
+
gem.name = 'strainer'
|
13
|
+
gem.require_paths = ['lib']
|
14
|
+
|
15
|
+
gem.add_runtime_dependency 'term-ansicolor', '~> 1.0.7'
|
16
|
+
|
17
|
+
gem.add_development_dependency 'yard', '~> 0.8.2'
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: strainer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Seth Vargo
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-13 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: term-ansicolor
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.0.7
|
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: 1.0.7
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: yard
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.8.2
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.8.2
|
46
|
+
description: Run isolated cookbook tests against your chef repository with Strainer.
|
47
|
+
email:
|
48
|
+
- sethvargo@gmail.com
|
49
|
+
executables:
|
50
|
+
- strain
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- .gitignore
|
55
|
+
- .rvmrc
|
56
|
+
- Gemfile
|
57
|
+
- LICENSE
|
58
|
+
- README.md
|
59
|
+
- Rakefile
|
60
|
+
- bin/strain
|
61
|
+
- lib/strainer.rb
|
62
|
+
- lib/strainer/cli.rb
|
63
|
+
- lib/strainer/color.rb
|
64
|
+
- lib/strainer/runner.rb
|
65
|
+
- lib/strainer/sandbox.rb
|
66
|
+
- strainer.gemspec
|
67
|
+
homepage: https://github.com/customink/strainer
|
68
|
+
licenses: []
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ! '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
requirements: []
|
86
|
+
rubyforge_project:
|
87
|
+
rubygems_version: 1.8.24
|
88
|
+
signing_key:
|
89
|
+
specification_version: 3
|
90
|
+
summary: Strainer allows you to run cookbook tests in an isolated environment while
|
91
|
+
still keeping a single Gemfile and repository for all your cookbooks.
|
92
|
+
test_files: []
|
93
|
+
has_rdoc:
|