chef-sandwich 0.2.0 → 0.3.0

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/NEWS CHANGED
@@ -1,4 +1,13 @@
1
1
  * sandwich NEWS
2
+ ** 0.3.0 (2011-10-17)
3
+
4
+ - Add -e option
5
+ - Add support for cookbooks: sandwich can now load other cookbooks
6
+ - Show chef version in --version output
7
+ - Change default log level from "warn" to "error"
8
+ - Change cookbook file lookup to work relative to sandwich scripts,
9
+ fixes #6
10
+
2
11
  ** 0.2.0 (2011-09-07)
3
12
 
4
13
  - Add support for cookbook_file and template resources
data/Rakefile CHANGED
@@ -1,6 +1,13 @@
1
+ require 'rake/testtask'
2
+
1
3
  namespace :gem do
2
4
  desc 'Build chef-sandwich gem'
3
5
  task :build do
4
6
  sh 'gem build chef-sandwich.gemspec'
5
7
  end
6
8
  end
9
+
10
+ Rake::TestTask.new do |t|
11
+ t.pattern = 'spec/*_spec.rb'
12
+ t.verbose = false
13
+ end
data/lib/sandwich/cli.rb CHANGED
@@ -10,7 +10,9 @@ module Sandwich
10
10
  @options = {}
11
11
 
12
12
  # default log level
13
- @options[:log_level] = :warn
13
+ @options[:log_level] = :error
14
+
15
+ @options[:command] = []
14
16
 
15
17
  @optparse = OptionParser.new do |opts|
16
18
  opts.banner = 'Usage: sandwich [options] [sandwichfile [arguments]]'
@@ -18,7 +20,8 @@ module Sandwich
18
20
  opts.on_tail('-v',
19
21
  '--version',
20
22
  'Show sandwich version') do
21
- puts "sandwich: #{Sandwich::Version}"
23
+ puts "sandwich: #{Sandwich::VERSION}"
24
+ puts "chef: #{Chef::VERSION}"
22
25
  exit
23
26
  end
24
27
 
@@ -34,6 +37,12 @@ module Sandwich
34
37
  'Set the log level (debug, info, warn, error, fatal)') do |l|
35
38
  @options[:log_level] = l.to_sym
36
39
  end
40
+
41
+ opts.on('-e',
42
+ '--command COMMAND',
43
+ 'Execute command as a sandwich script') do |c|
44
+ @options[:command] << c
45
+ end
37
46
  end
38
47
  end
39
48
 
@@ -44,21 +53,27 @@ module Sandwich
44
53
  def run(argv)
45
54
  unparsed_arguments = @optparse.order!(argv)
46
55
 
47
- # use first argument as sandwich script filename...
48
- recipe_filename = unparsed_arguments.shift
49
-
50
- # ...check for stdin...
51
- if recipe_filename.nil? || recipe_filename == '-'
52
- recipe_filename = '<STDIN>'
53
- recipe_file = STDIN.read
56
+ # check for -e commands
57
+ if @options[:command].any?
58
+ recipe_filename = '<COMMAND>'
59
+ recipe = @options[:command].join("\n")
54
60
  else
55
- recipe_file = File.read(recipe_filename)
61
+ # use first argument as sandwich script filename...
62
+ recipe_filename = unparsed_arguments.shift
63
+
64
+ # check for stdin
65
+ if recipe_filename.nil? || recipe_filename == '-'
66
+ recipe_filename = '<STDIN>'
67
+ recipe = STDIN.read
68
+ else
69
+ recipe = nil
70
+ end
56
71
  end
57
72
 
58
- # ...and pass remaining arguments on to script
73
+ # pass remaining arguments on to script
59
74
  ARGV.replace(unparsed_arguments)
60
- runner = Sandwich::Runner.new(recipe_file, recipe_filename)
61
75
 
76
+ runner = Sandwich::Runner.new(recipe_filename, recipe)
62
77
  runner.run(@options[:log_level])
63
78
  end
64
79
  end
@@ -0,0 +1,82 @@
1
+ require 'sandwich/cookbook_version'
2
+ require 'chef'
3
+
4
+ module Sandwich
5
+ # Chef::Client extended to inject a Sandwich cookbook into the
6
+ # client's run context
7
+ class Client < Chef::Client
8
+ # Create a new instance of Client.
9
+ #
10
+ # File specified by +recipe_filename+ is only read if no
11
+ # +recipe_string+ is supplied, otherwise +recipe_string+ is used
12
+ # as a recipe and +recipe_filename+ is only used in log messages.
13
+ #
14
+ # @param [String] recipe_filename the recipe filename
15
+ # @param [String] recipe_string the recipe definition
16
+ def initialize(recipe_filename, recipe_string = nil)
17
+ Chef::Config[:solo] = true
18
+ super()
19
+
20
+ if recipe_string
21
+ @sandwich_basedir = Dir.getwd
22
+ else
23
+ @sandwich_basedir = File.expand_path(File.dirname(recipe_filename))
24
+ recipe_string = File.read(recipe_filename)
25
+ end
26
+
27
+ add_cookbook_dir(@sandwich_basedir)
28
+
29
+ @sandwich_cookbook_name = unique_cookbook_name
30
+ @sandwich_recipe = recipe_string
31
+ @sandwich_filename = recipe_filename
32
+ end
33
+
34
+ # Silently build a new node object for this client
35
+ #
36
+ # @return [Chef::Node] the created node object
37
+ def build_node
38
+ # silence build_node from super class
39
+ silence_chef { super }
40
+ end
41
+
42
+ # Set up the client's run context, inject Sandwich cookbook into
43
+ # the created run context
44
+ #
45
+ # @return [Chef::RunContext] the created run context
46
+ def setup_run_context
47
+ run_context = super
48
+ cookbook = Sandwich::CookbookVersion.new(@sandwich_cookbook_name,
49
+ @sandwich_basedir)
50
+ run_context.cookbook_collection[@sandwich_cookbook_name] = cookbook
51
+ recipe = Chef::Recipe.new(@sandwich_cookbook_name, nil, run_context)
52
+ recipe.from_string(@sandwich_recipe, @sandwich_filename)
53
+ run_context
54
+ end
55
+
56
+ # Add a cookbook directory to the front of the client's cookbook
57
+ # search path.
58
+ #
59
+ # @param [String] cookbook_dir the cookbook directory to add
60
+ # @return [Array] the new cookbook search path
61
+ def add_cookbook_dir(cookbook_dir)
62
+ Chef::Config[:cookbook_path].unshift(cookbook_dir)
63
+ end
64
+
65
+ private
66
+ def silence_chef
67
+ log_level = Chef::Log.level
68
+ Chef::Log.level = :fatal
69
+ yield
70
+ ensure
71
+ Chef::Log.level = log_level
72
+ end
73
+
74
+ # monkey patch: don't check for empty cookbook paths
75
+ def assert_cookbook_path_not_empty(run_context); end
76
+
77
+ def unique_cookbook_name
78
+ # use uuid in sandwich cookbook name to avoid name collisions
79
+ "sandwich_#{UUIDTools::UUID.random_create}"
80
+ end
81
+ end
82
+ end
@@ -1,21 +1,31 @@
1
1
  require 'chef/cookbook_version'
2
2
 
3
- # Chef::CookbookVersion, monkey patched to use simpler file source
4
- # paths (always uses local files instead of manifest records)
5
- class Chef::CookbookVersion
6
- # Determines the absolute source filename on disk for various file
7
- # resources from their relative path
8
- #
9
- # @param [Chef::Node] node the node object, ignored
10
- # @param [Symbol] segment the segment of the current resource, ignored
11
- # @param [String] filename the source file path
12
- # @param [String] current_filepath the target file path, ignored
13
- # @return [String] the preferred source filename
14
- def preferred_filename_on_disk_location(node,
15
- segment,
16
- filename,
17
- current_filepath=nil)
18
- # keep absolute paths, convert relative paths into absolute paths
19
- filename.start_with?('/') ? filename : File.join(Dir.getwd, filename)
3
+ module Sandwich
4
+ # Chef::CookbookVersion, extended to use simpler file source paths
5
+ # (always uses local files relative to @basedir instead of manifest
6
+ # records)
7
+ class CookbookVersion < Chef::CookbookVersion
8
+ # @param [String] name the cookbook's name
9
+ # @param [String basedir the cookbook's basedir
10
+ def initialize(name, basedir)
11
+ super(name)
12
+ @basedir = basedir
13
+ end
14
+
15
+ # Determines the absolute source filename on disk for various file
16
+ # resources from their relative path
17
+ #
18
+ # @param [Chef::Node] node the node object, ignored
19
+ # @param [Symbol] segment the segment of the current resource, ignored
20
+ # @param [String] filename the source file path
21
+ # @param [String] current_filepath the target file path, ignored
22
+ # @return [String] the preferred source filename
23
+ def preferred_filename_on_disk_location(node,
24
+ segment,
25
+ filename,
26
+ current_filepath=nil)
27
+ # keep absolute paths, convert relative paths into absolute paths
28
+ filename.start_with?('/') ? filename : File.join(@basedir, filename)
29
+ end
20
30
  end
21
31
  end
@@ -1,19 +1,21 @@
1
- require 'chef'
1
+ require 'chef/config'
2
2
  require 'sandwich/recipe'
3
- require 'sandwich/cookbook_version'
3
+ require 'sandwich/client'
4
4
 
5
5
  module Sandwich
6
6
  # This class constructs a {Chef::Recipe} from a recipe string and
7
- # applies it with Chef standalone mode
7
+ # applies it with Chef standalone mode.
8
8
  class Runner
9
+ # Create a new instance of Runner.
10
+ #
11
+ # File specified by +recipe_filename+ is only read if no
12
+ # +recipe_string+ is supplied, otherwise +recipe_string+ is used
13
+ # as a recipe and +recipe_filename+ is only used in log messages.
14
+ #
15
+ # @param [String] recipe_filename the recipe filename
9
16
  # @param [String] recipe_string the recipe definition
10
- def initialize(recipe_string, filename)
11
- @client = solo_client
12
- cookbook_name = 'sandwich'
13
- cookbook_collection = single_cookbook_collection(cookbook_name)
14
- @run_context = Chef::RunContext.new(@client.node, cookbook_collection)
15
- @recipe = Chef::Recipe.new(cookbook_name, nil, @run_context)
16
- @recipe.from_string(recipe_string, filename)
17
+ def initialize(recipe_filename, recipe_string = nil)
18
+ @client = Sandwich::Client.new(recipe_filename, recipe_string)
17
19
  end
18
20
 
19
21
  # Run Chef in standalone mode, apply recipe
@@ -25,17 +27,10 @@ module Sandwich
25
27
  # @return [void]
26
28
  def run(log_level = :warn)
27
29
  configure_chef(log_level)
28
- Chef::Runner.new(@run_context).converge
30
+ @client.run
29
31
  end
30
32
 
31
33
  private
32
- def solo_client
33
- Chef::Config[:solo] = true
34
- client = Chef::Client.new
35
- client.run_ohai
36
- client.build_node
37
- end
38
-
39
34
  def configure_chef(log_level)
40
35
  Chef::Log.level = log_level
41
36
 
@@ -46,11 +41,5 @@ module Sandwich
46
41
  Chef::Config[:file_backup_path] = File.join(local_cache, 'backup')
47
42
  end
48
43
  end
49
-
50
- # create a cookbook collection containing a single empty cookbook
51
- def single_cookbook_collection(cookbook_name)
52
- cookbook = Chef::CookbookVersion.new(cookbook_name)
53
- Chef::CookbookCollection.new({ cookbook_name => cookbook })
54
- end
55
44
  end
56
45
  end
@@ -1,4 +1,4 @@
1
1
  module Sandwich
2
2
  # the current version of sandwich
3
- Version = '0.2.0'
3
+ VERSION = '0.3.0'
4
4
  end
@@ -0,0 +1,59 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__)
2
+ require 'spec_helper'
3
+
4
+ describe Sandwich::Runner do
5
+ after(:each) do
6
+ FakeFS::FileSystem.clear
7
+ end
8
+
9
+ describe Chef::Resource::File do
10
+ it 'should create files' do
11
+ with_fakefs do
12
+ filename = '/foo'
13
+ content = 'hello world'
14
+ recipe = %Q(file '#{filename}' do content '#{content}';end)
15
+ run_recipe(recipe)
16
+ file = File.read(filename)
17
+ file.must_equal content
18
+ end
19
+ end
20
+
21
+ it 'should throw exceptions for files in missing directories' do
22
+ with_fakefs do
23
+ filename = '/i/am/not/here'
24
+ content = 'hello world'
25
+ recipe = %Q(file '#{filename}' do content '#{content}';end)
26
+ run = Proc.new { run_recipe recipe }
27
+ run.must_raise(Errno::ENOENT,
28
+ Chef::Exceptions::EnclosingDirectoryDoesNotExist)
29
+ end
30
+ end
31
+ end
32
+
33
+ describe Chef::Resource::CookbookFile do
34
+ it 'should create cookbook files' do
35
+ with_fakefs do
36
+ source = '/source'
37
+ target = '/target'
38
+ content = 'hello world'
39
+ recipe = %Q(cookbook_file '#{target}' do source '#{source}';end)
40
+ # create source for cookbook file
41
+ File.open(source, 'w') { |f| f.write(content) }
42
+ run_recipe(recipe)
43
+ File.read(source).must_equal File.read(target)
44
+ end
45
+ end
46
+ end
47
+
48
+ describe Chef::Resource::Directory do
49
+ it 'should create directories' do
50
+ with_fakefs do
51
+ dir = '/foo'
52
+ recipe = %Q(directory '#{dir}')
53
+ assert !Dir.exists?(dir)
54
+ run_recipe(recipe)
55
+ assert Dir.exists?(dir)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,47 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'fileutils'
4
+ require 'fakefs/safe'
5
+ require 'sandwich/runner'
6
+ require 'chef'
7
+
8
+ # Ohai::System, monkey patched to return static values
9
+ class Ohai::System
10
+ def all_plugins
11
+ @data = Mash.new({ :hostname => 'archie',
12
+ :platform => 'ubuntu',
13
+ :fqdn => 'archie.example.com',
14
+ :platform_version => '11.04',
15
+ :os_version => '2.6.38-10-generic' })
16
+ end
17
+ end
18
+
19
+ # monkey patch for https://github.com/defunkt/fakefs/issues/96
20
+ class FakeFS::Dir
21
+ def self.mkdir(path, integer = 0)
22
+ FileUtils.mkdir(path)
23
+ end
24
+ end
25
+
26
+ def runner_from_recipe(recipe)
27
+ Sandwich::Runner.new('<SPEC_HELPER>', recipe)
28
+ end
29
+
30
+ def run_recipe(recipe)
31
+ runner_from_recipe(recipe).run(:fatal)
32
+ end
33
+
34
+ def with_fakefs
35
+ FakeFS.activate!
36
+ setup_standard_dirs
37
+ yield
38
+ ensure
39
+ FakeFS.deactivate!
40
+ end
41
+
42
+ def setup_standard_dirs
43
+ FileUtils.mkdir_p '/tmp'
44
+ end
45
+
46
+ # make sure Chef 0.10 exceptions are available when using older Chef versions
47
+ class Chef::Exceptions::EnclosingDirectoryDoesNotExist; end
metadata CHANGED
@@ -1,13 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chef-sandwich
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
5
- prerelease: false
6
- segments:
7
- - 0
8
- - 2
9
- - 0
10
- version: 0.2.0
4
+ prerelease:
5
+ version: 0.3.0
11
6
  platform: ruby
12
7
  authors:
13
8
  - Sebastian Boehm
@@ -15,7 +10,7 @@ autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
12
 
18
- date: 2011-09-07 00:00:00 +02:00
13
+ date: 2011-10-17 00:00:00 +02:00
19
14
  default_executable:
20
15
  dependencies:
21
16
  - !ruby/object:Gem::Dependency
@@ -26,13 +21,53 @@ dependencies:
26
21
  requirements:
27
22
  - - ">="
28
23
  - !ruby/object:Gem::Version
29
- hash: 25
30
- segments:
31
- - 0
32
- - 9
33
24
  version: "0.9"
34
25
  type: :runtime
35
26
  version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: uuidtools
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: "0"
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: minitest
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: fakefs
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ type: :development
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: rake
62
+ prerelease: false
63
+ requirement: &id005 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 0.8.7
69
+ type: :development
70
+ version_requirements: *id005
36
71
  description: |
37
72
  Sandwich lets you apply Chef recipes to your system without having to
38
73
  worry about cookbooks or configuration.
@@ -50,13 +85,16 @@ files:
50
85
  - LICENSE
51
86
  - NEWS
52
87
  - bin/sandwich
53
- - lib/sandwich.rb
54
- - lib/sandwich/version.rb
55
88
  - lib/sandwich/cli.rb
56
- - lib/sandwich/recipe.rb
89
+ - lib/sandwich/client.rb
57
90
  - lib/sandwich/cookbook_version.rb
91
+ - lib/sandwich/recipe.rb
58
92
  - lib/sandwich/runner.rb
59
- has_rdoc: yard
93
+ - lib/sandwich/version.rb
94
+ - lib/sandwich.rb
95
+ - spec/runner_spec.rb
96
+ - spec/spec_helper.rb
97
+ has_rdoc: true
60
98
  homepage: https://github.com/sometimesfood/sandwich
61
99
  licenses: []
62
100
 
@@ -70,23 +108,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
70
108
  requirements:
71
109
  - - ">="
72
110
  - !ruby/object:Gem::Version
73
- hash: 3
74
- segments:
75
- - 0
76
111
  version: "0"
77
112
  required_rubygems_version: !ruby/object:Gem::Requirement
78
113
  none: false
79
114
  requirements:
80
115
  - - ">="
81
116
  - !ruby/object:Gem::Version
82
- hash: 3
83
- segments:
84
- - 0
85
117
  version: "0"
86
118
  requirements: []
87
119
 
88
120
  rubyforge_project:
89
- rubygems_version: 1.3.7
121
+ rubygems_version: 1.6.2
90
122
  signing_key:
91
123
  specification_version: 3
92
124
  summary: The easiest way to get started as a chef