strainer 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.3@strainer --create
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
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.
@@ -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
@@ -0,0 +1,2 @@
1
+ #! /usr/bin/env rake
2
+ require 'bundler/gem_tasks'
@@ -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)
@@ -0,0 +1,9 @@
1
+ require 'strainer/color'
2
+ require 'strainer/runner'
3
+ require 'strainer/sandbox'
4
+
5
+ module Strainer
6
+ def self.root
7
+ @@root ||= File.expand_path('../../', __FILE__)
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ module Strainer
2
+ class CLI
3
+ def self.run(*cookbooks)
4
+ @sandbox = Strainer::Sandbox.new(cookbooks)
5
+ @runner = Strainer::Runner.new(@sandbox)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ require 'term/ansicolor'
2
+
3
+ module Strainer
4
+ class Color
5
+ extend Term::ANSIColor
6
+ end
7
+ end
@@ -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
@@ -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: