chef-sandwich 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/NEWS +9 -0
- data/Rakefile +7 -0
- data/lib/sandwich/cli.rb +27 -12
- data/lib/sandwich/client.rb +82 -0
- data/lib/sandwich/cookbook_version.rb +27 -17
- data/lib/sandwich/runner.rb +13 -24
- data/lib/sandwich/version.rb +1 -1
- data/spec/runner_spec.rb +59 -0
- data/spec/spec_helper.rb +47 -0
- metadata +55 -23
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
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] = :
|
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::
|
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
|
-
#
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
|
4
|
-
#
|
5
|
-
|
6
|
-
#
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
#
|
19
|
-
|
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
|
data/lib/sandwich/runner.rb
CHANGED
@@ -1,19 +1,21 @@
|
|
1
|
-
require 'chef'
|
1
|
+
require 'chef/config'
|
2
2
|
require 'sandwich/recipe'
|
3
|
-
require 'sandwich/
|
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(
|
11
|
-
@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
|
-
|
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
|
data/lib/sandwich/version.rb
CHANGED
data/spec/runner_spec.rb
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|
-
|
5
|
-
|
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-
|
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/
|
89
|
+
- lib/sandwich/client.rb
|
57
90
|
- lib/sandwich/cookbook_version.rb
|
91
|
+
- lib/sandwich/recipe.rb
|
58
92
|
- lib/sandwich/runner.rb
|
59
|
-
|
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.
|
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
|