Skip to content
This repository was archived by the owner on Jan 15, 2024. It is now read-only.

Commit

Permalink
Support standalone mode
Browse files Browse the repository at this point in the history
  • Loading branch information
sethvargo committed Apr 10, 2013
1 parent 77590de commit b5f68e1
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 121 deletions.
2 changes: 1 addition & 1 deletion lib/strainer/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def run!
inside_sandbox do
Strainer.ui.debug "Running '#{command}'"
speak command
PTY.spawn command do |r, _, pid|
PTY.spawn(command) do |r, _, pid|
begin
r.sync
r.each_line { |line| speak line }
Expand Down
308 changes: 188 additions & 120 deletions lib/strainer/sandbox.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,155 +18,223 @@ class Sandbox
# a list of options to pass along
def initialize(cookbook_names, options = {})
@options = options
@cookbooks = load_cookbooks(cookbook_names)

if chef_repo?
@cookbooks = load_cookbooks(cookbook_names)
elsif cookbook_repo?
unless cookbook_names.empty?
Strainer.ui.warn "Supply specific cookbooks to a cookbook_repo is not supported. Use `strainer test` with no arguments instead."
end
@cookbooks = [load_self]
end

reset_sandbox
copy_cookbooks
end

# Clear out the existing sandbox and create the directories
def reset_sandbox
Strainer.ui.debug "Resetting sandbox..."
destroy_sandbox
create_sandbox
end
private
# Clear out the existing sandbox and create the directories
def reset_sandbox
Strainer.ui.debug "Resetting sandbox..."
destroy_sandbox
create_sandbox
end

# Destroy the current sandbox, if it exists
def destroy_sandbox
if File.directory?(SANDBOX)
Strainer.ui.debug " Destroying sandbox at '#{SANDBOX}'"
FileUtils.rm_rf(SANDBOX)
# Destroy the current sandbox, if it exists
def destroy_sandbox
if File.directory?(SANDBOX)
Strainer.ui.debug " Destroying sandbox at '#{SANDBOX}'"
FileUtils.rm_rf(SANDBOX)
end
end
end

# Create the sandbox unless it already exits
def create_sandbox
unless File.directory?(SANDBOX)
Strainer.ui.debug " Creating sandbox at '#{SANDBOX}'"
FileUtils.mkdir_p(SANDBOX)
copy_globals
place_knife_rb
# Create the sandbox unless it already exits
def create_sandbox
unless File.directory?(SANDBOX)
Strainer.ui.debug " Creating sandbox at '#{SANDBOX}'"
FileUtils.mkdir_p(SANDBOX)
copy_globals
place_knife_rb
end
end
end

# Copy over a whitelist of common files into our sandbox
def copy_globals
files = Dir[*%W(#{Strainer.strainerfile_name} foodcritic .rspec spec test)]
Strainer.ui.debug "Copying '#{files}' to '#{SANDBOX}'"
FileUtils.cp_r(files, SANDBOX)
end
# Copy over a whitelist of common files into our sandbox
def copy_globals
if chef_repo?
files = Dir[*%W(#{Strainer.strainerfile_name} foodcritic .rspec spec test)]
elsif cookbook_repo?
files = Dir[*%W(#{Strainer.strainerfile_name} foodcritic .rspec)]
else
files = []
end

# Create a basic knife.rb file to ensure tests run successfully
def place_knife_rb
chef_path = SANDBOX.join('.chef')
Strainer.ui.debug "Copying '#{files}' to '#{SANDBOX}'"
FileUtils.cp_r(files, SANDBOX)
end

Strainer.ui.debug "Creating directory '#{chef_path}'"
FileUtils.mkdir_p(chef_path)
# Create a basic knife.rb file to ensure tests run successfully
def place_knife_rb
chef_path = SANDBOX.join('.chef')

# Build the contents
contents = <<-EOH
cache_type 'BasicFile'
cache_options(:path => "\#{ENV['HOME']}/.chef/checksums")
cookbook_path '#{SANDBOX}'
EOH
Strainer.ui.debug "Creating directory '#{chef_path}'"
FileUtils.mkdir_p(chef_path)

# Create knife.rb
Strainer.ui.debug "Writing '#{chef_path}/knife.rb' with content: \n\n#{contents}\n"
File.open("#{chef_path}/knife.rb", 'w+'){ |f| f.write(contents) }
end
# Build the contents
contents = <<-EOH
cache_type 'BasicFile'
cache_options(:path => "\#{ENV['HOME']}/.chef/checksums")
cookbook_path '#{SANDBOX}'
EOH

# Create knife.rb
Strainer.ui.debug "Writing '#{chef_path}/knife.rb' with content: \n\n#{contents}\n"
File.open("#{chef_path}/knife.rb", 'w+'){ |f| f.write(contents) }
end

# Copy all the cookbooks provided in {#initialize} to the isolated sandbox location
def copy_cookbooks
cookbooks_and_dependencies.each do |cookbook|
sandbox_path = SANDBOX.join(cookbook.cookbook_name)
# Copy all the cookbooks provided in {#initialize} to the isolated sandbox location
def copy_cookbooks
cookbooks_and_dependencies.each do |cookbook|
sandbox_path = SANDBOX.join(cookbook.cookbook_name)

# Copy the files to our sandbox
Strainer.ui.debug "Copying '#{cookbook.name}' to '#{sandbox_path}'"
FileUtils.cp_r(cookbook.path, sandbox_path)
# Copy the files to our sandbox
Strainer.ui.debug "Copying '#{cookbook.name}' to '#{sandbox_path}'"
FileUtils.cp_r(cookbook.path, sandbox_path)

# Override the @path location so we don't need to create a new object
cookbook.path = sandbox_path
# Override the @path location so we don't need to create a new object
cookbook.path = sandbox_path
end
end
end

# Load a cookbook from the given array of cookbook names
#
# @param [Array<String>] cookbook_names
# the list of cookbooks to search for
# @return [Array<Berkshelf::CachedCookbook>]
# the array of cached cookbooks
def load_cookbooks(cookbook_names)
cookbook_names.collect{ |cookbook_name| load_cookbook(cookbook_name) }
end
# Load a cookbook from the given array of cookbook names
#
# @param [Array<String>] cookbook_names
# the list of cookbooks to search for
# @return [Array<Berkshelf::CachedCookbook>]
# the array of cached cookbooks
def load_cookbooks(cookbook_names)
cookbook_names.collect{ |cookbook_name| load_cookbook(cookbook_name) }
end

# Load an individual cookbook by its name
#
# @param [String] cookbook_name
# the name of the cookbook to load
# @return [Berkshelf::CachedCookbook]
# the cached cookbook
# @raise [Strainer::Error::CookbookNotFound]
# when the cookbook was not found in any of the sources
def load_cookbook(cookbook_name)
Strainer.ui.debug "Sandbox#load_cookbook('#{cookbook_name}')"
cookbook_path = cookbooks_paths.find { |path| path.join(cookbook_name).exist? }

cookbook = if cookbook_path
path = cookbook_path.join(cookbook_name)
Strainer.ui.debug " found cookbook at '#{path}'"
# Load an individual cookbook by its name
#
# @param [String] cookbook_name
# the name of the cookbook to load
# @return [Berkshelf::CachedCookbook]
# the cached cookbook
# @raise [Strainer::Error::CookbookNotFound]
# when the cookbook was not found in any of the sources
def load_cookbook(cookbook_name)
Strainer.ui.debug "Sandbox#load_cookbook('#{cookbook_name}')"
cookbook_path = cookbooks_paths.find { |path| path.join(cookbook_name).exist? }

cookbook = if cookbook_path
path = cookbook_path.join(cookbook_name)
Strainer.ui.debug " found cookbook at '#{path}'"

begin
Berkshelf::CachedCookbook.from_path(path)
rescue Berkshelf::CookbookNotFound
raise Strainer::Error::CookbookNotFound, "'#{path}' existed, but I could not extract a cookbook. Is there a 'metadata.rb'?"
end
else
Strainer.ui.debug " did not find '#{cookbook_name}' in any of the sources - resorting to the default cookbook_store..."
Berkshelf.cookbook_store.cookbooks(cookbook_name).last
end

cookbook || raise(Strainer::Error::CookbookNotFound, "Could not find '#{cookbook_name}' in any of the sources.")
end

# Load the current root entirely as a cookbook. This is useful when testing within
# a cookbook, instead of a chef repo
def load_self
Strainer.ui.debug "Sandbox#load_self"

begin
::Berkshelf::CachedCookbook.from_path(path)
rescue ::Berkshelf::CookbookNotFound
raise Strainer::Error::CookbookNotFound, "'#{path}' existed, but I could not extract a cookbook. Is there a 'metadata.rb'?"
Berkshelf::CachedCookbook.from_path(File.expand_path('.'))
rescue Berkshelf::CookbookNotFound
raise Strainer::Error::CookbookNotFound, "'#{File.expand_path('.')}' existed, but I could not extract a cookbook. Is there a 'metadata.rb'?"
end
else
Strainer.ui.debug " did not find '#{cookbook_name}' in any of the sources - resorting to the default cookbook_store..."
Berkshelf.cookbook_store.cookbooks(cookbook_name).last
end

cookbook || raise(Strainer::Error::CookbookNotFound, "Could not find '#{cookbook_name}' in any of the sources.")
end

# Dynamically builds a list of possible cookbook paths from the
# `@options` hash, Berkshelf config, and Chef config, and a logical
# guess
#
# @return [Array<Pathname>]
# a list of possible cookbook locations
def cookbooks_paths
@cookbooks_paths ||= begin
paths = [
@options[:cookbooks_path],
Berkshelf::Chef::Config.instance[:cookbook_path],
'cookbooks'
].flatten.compact.map{ |path| Pathname.new(File.expand_path(path)) }.uniq

paths.select!{ |path| File.exists?(path) }
Strainer.ui.debug "Setting Sandbox#cookbooks_paths to #{paths.map(&:to_s)}"
paths
# Dynamically builds a list of possible cookbook paths from the
# `@options` hash, Berkshelf config, and Chef config, and a logical
# guess
#
# @return [Array<Pathname>]
# a list of possible cookbook locations
def cookbooks_paths
@cookbooks_paths ||= begin
paths = [
@options[:cookbooks_path],
Berkshelf::Chef::Config.instance[:cookbook_path],
'cookbooks'
].flatten.compact.map{ |path| Pathname.new(File.expand_path(path)) }.uniq

paths.select!{ |path| File.exists?(path) }
Strainer.ui.debug "Setting Sandbox#cookbooks_paths to #{paths.map(&:to_s)}"
paths
end
end
end

# Collect all cookbooks and the dependencies specified in their metadata.rb
# for copying
#
# @return [Array<Berkshelf::CachedCookbook>]
# a list of cached cookbooks
def cookbooks_and_dependencies
loaded_dependencies = Hash.new(false)

dependencies = @cookbooks.dup
dependencies.each do |cookbook|
loaded_dependencies[cookbook.cookbook_name] = true

cookbook.metadata.dependencies.keys.each do |dependency_name|
unless loaded_dependencies[dependency_name]
dependencies << load_cookbook(dependency_name)
loaded_dependencies[dependency_name] = true
# Collect all cookbooks and the dependencies specified in their metadata.rb
# for copying
#
# @return [Array<Berkshelf::CachedCookbook>]
# a list of cached cookbooks
def cookbooks_and_dependencies
loaded_dependencies = Hash.new(false)

dependencies = @cookbooks.dup
dependencies.each do |cookbook|
loaded_dependencies[cookbook.cookbook_name] = true

cookbook.metadata.dependencies.keys.each do |dependency_name|
unless loaded_dependencies[dependency_name]
dependencies << load_cookbook(dependency_name)
loaded_dependencies[dependency_name] = true
end
end
end
end
end

# Determines if the current project is a cookbook repo
#
# @return [Boolean]
# true if the current project is a cookbook repo, false otherwise
def cookbook_repo?
@_cookbook_repo ||= File.exists?('metadata.rb')
end

# Determines if the current project is a chef repo
#
# @return [Boolean]
# true if the current project is a chef repo, false otherwise
def chef_repo?
@_chef_repo ||= begin
chef_folders = %w(.chef certificates config cookbooks data_bags environments roles)
(root_folders & chef_folders).size > 4
end
end

# Return a list of all directory folders at the root of the repo.
# This is useful for detecting if it's a chef repo or cookbook
# repo.
#
# @return [Array]
# the list of root-level directories
def root_folders
@root_folders ||= Dir.glob("#{Dir.pwd}/*", File::FNM_DOTMATCH).tap { |a| a.shift(2) }.collect do |f|
File.basename(f) if File.directory?(f)
end.compact
end

# Determine if the current project is a git repo?
#
# @return [Boolean]
# true if a .git directory is found, false otherwise
def git_repo?
@_git_repo ||= root_folders.include?('.git')
end

end
end

0 comments on commit b5f68e1

Please sign in to comment.