sous_chef 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,25 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ gbin
18
+ coverage
19
+ doc
20
+ rdoc
21
+ pkg
22
+ vendor/gems
23
+ .yardoc
24
+
25
+ ## PROJECT::SPECIFIC
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ bin_path 'gbin'
2
+ disable_system_gems
3
+
4
+ only :runtime do
5
+ end
6
+
7
+ only :development do
8
+ gem 'bundler'
9
+ gem 'jeweler'
10
+ gem 'rspec', :require_as => 'spec'
11
+ gem 'ZenTest'
12
+ gem 'rake'
13
+ gem 'rcov'
14
+ gem 'ruby-debug'
15
+ gem 'yard'
16
+ gem 'reek'
17
+ gem 'roodi'
18
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Martin Emde
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,6 @@
1
+ = Sous Chef
2
+
3
+ Chef's prep-cook
4
+
5
+ Create bash scripts with chef-like syntax
6
+
data/Rakefile ADDED
@@ -0,0 +1,76 @@
1
+ require 'bundler'
2
+ Bundler.require_env
3
+ require 'rake'
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gem|
8
+ gem.name = "sous_chef"
9
+ gem.summary = %Q{Chef's prep-cook}
10
+ gem.description = %Q{Create bash scripts using a simple chef-like syntax}
11
+ gem.email = ["memde@engineyard.com", "bdonovan@engineyard.com"]
12
+ gem.homepage = "http://github.com/engineyard/sous_chef"
13
+ gem.authors = ["Martin Emde", "Brian Donovan"]
14
+
15
+ bundle = Bundler::Bundle.load(File.dirname(__FILE__) + '/Gemfile')
16
+ bundle.environment.dependencies.each do |d|
17
+ if d.only && d.only.include?('runtime')
18
+ gem.add_dependency d.name, d.version.to_s
19
+ end
20
+ end
21
+ end
22
+ Jeweler::GemcutterTasks.new
23
+ rescue LoadError
24
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
25
+ end
26
+
27
+ require 'spec/rake/spectask'
28
+ Spec::Rake::SpecTask.new(:spec) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.spec_files = FileList['spec/**/*_spec.rb']
31
+ end
32
+
33
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
34
+ spec.libs << 'lib' << 'spec'
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ spec.rcov_opts << '--exclude' << 'spec,vendor,Library'
38
+ end
39
+
40
+ task :spec => :check_dependencies
41
+
42
+ begin
43
+ require 'reek/adapters/rake_task'
44
+ Reek::RakeTask.new do |t|
45
+ t.fail_on_error = true
46
+ t.verbose = false
47
+ t.source_files = 'lib/**/*.rb'
48
+ end
49
+ rescue LoadError
50
+ task :reek do
51
+ abort "Reek is not available. In order to run reek, you must: sudo gem install reek"
52
+ end
53
+ end
54
+
55
+ begin
56
+ require 'roodi'
57
+ require 'roodi_task'
58
+ RoodiTask.new do |t|
59
+ t.verbose = false
60
+ end
61
+ rescue LoadError
62
+ task :roodi do
63
+ abort "Roodi is not available. In order to run roodi, you must: sudo gem install roodi"
64
+ end
65
+ end
66
+
67
+ task :default => :spec
68
+
69
+ begin
70
+ require 'yard'
71
+ YARD::Rake::YardocTask.new
72
+ rescue LoadError
73
+ task :yardoc do
74
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
75
+ end
76
+ end
data/VERSION.yml ADDED
@@ -0,0 +1,5 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 0
4
+ :patch: 1
5
+
@@ -0,0 +1,114 @@
1
+ module SousChef
2
+ class Recipe
3
+ attr_accessor :node, :verbose, :shebang
4
+ alias_method :verbose?, :verbose
5
+ alias_method :shebang?, :shebang
6
+
7
+ def initialize(*flags, &block)
8
+ flags.each {|flag| __send__("#{flag}=", true)}
9
+ @resources = []
10
+ @block = block
11
+ end
12
+
13
+ def self.load(file)
14
+ new { instance_eval(File.read(file), file, 1) }
15
+ end
16
+
17
+ def setup
18
+ if @block
19
+ instance_eval &@block
20
+ @block = nil
21
+ end
22
+ end
23
+
24
+ def to_script
25
+ @script ||= begin
26
+ setup
27
+ lines = []
28
+ lines << "#!/bin/bash" if shebang?
29
+ lines += @resources.map do |resource|
30
+ # @context = resource
31
+ script = ""
32
+ script << %{# #{resource.name}\n} if verbose? && resource.name
33
+ script << resource.to_script
34
+ end
35
+ lines.join("\n\n")
36
+ end
37
+ end
38
+
39
+ def echo(string)
40
+ command "echo '#{escape_string(string)}'"
41
+ end
42
+
43
+ def execute(*args, &block)
44
+ add_resource Resource::Execute.new(self, *args, &block)
45
+ end
46
+
47
+ def file(*args, &block)
48
+ add_resource Resource::File.new(self, *args, &block)
49
+ end
50
+
51
+ def directory(*args, &block)
52
+ add_resource Resource::Directory.new(self, *args, &block)
53
+ end
54
+
55
+ def log(*args, &block)
56
+ add_resource Resource::Log.new(self, *args, &block)
57
+ end
58
+
59
+ def gemfile(*args, &block)
60
+ add_resource Resource::Gemfile.new(self, *args, &block)
61
+ end
62
+
63
+ def command(cmd)
64
+ if context
65
+ context.command(cmd)
66
+ else
67
+ execute { command cmd }
68
+ end
69
+ end
70
+
71
+ def halt_on_failed_command
72
+ execute "halt on failed command" do
73
+ command "set -e"
74
+ end
75
+ end
76
+
77
+ protected
78
+ def add_resource(resource)
79
+ with_context(resource) do
80
+ @resources << resource
81
+ resource.setup
82
+ end
83
+ resource
84
+ end
85
+
86
+ def context
87
+ @context
88
+ end
89
+
90
+ def with_context(resource)
91
+ @context = resource
92
+ yield
93
+ @context = nil
94
+ end
95
+
96
+ def escape_path(path)
97
+ path
98
+ end
99
+
100
+ def escape_string(string)
101
+ # many slashes because single-quote has some sort of
102
+ # special meaning in regexp replacement strings
103
+ string && string.gsub(/'/, %q{\\\\'})
104
+ end
105
+
106
+ def method_missing(meth, *args, &block)
107
+ if context && context.resource_respond_to?(meth)
108
+ context.__send__(meth, *args, &block)
109
+ else
110
+ super
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,79 @@
1
+ module SousChef
2
+ module Resource
3
+ class Base
4
+ attr_reader :name
5
+
6
+ def initialize(context, name, &block)
7
+ @context = context
8
+ @name = name
9
+ @block = block
10
+ @only_if = nil
11
+ @commands = []
12
+ end
13
+
14
+ def setup
15
+ if @block
16
+ instance_eval &@block
17
+ @block = nil
18
+ end
19
+ end
20
+
21
+ def not_if(cmd=nil)
22
+ only_if "! #{cmd}"
23
+ end
24
+
25
+ def only_if(cmd=nil)
26
+ if cmd
27
+ @only_if = cmd
28
+ else
29
+ @only_if
30
+ end
31
+ end
32
+
33
+ alias_method :resource_respond_to?, :respond_to?
34
+ def respond_to?(meth)
35
+ super || context.respond_to?(meth)
36
+ end
37
+
38
+ def prepend(cmd)
39
+ @commands.unshift(cmd)
40
+ end
41
+
42
+ def append(cmd)
43
+ @commands.push(cmd)
44
+ end
45
+
46
+ alias_method :command, :append
47
+
48
+ def to_script
49
+ setup
50
+ if only_if
51
+ @commands.compact!
52
+ @commands.map! { |line| " #{line}" }
53
+ prepend "if #{only_if}; then"
54
+ append "fi"
55
+ end
56
+ @commands.join("\n").strip
57
+ end
58
+
59
+ protected
60
+ attr_reader :context
61
+
62
+ def set_or_return(attr, val)
63
+ if val.nil?
64
+ instance_variable_get("@#{attr}")
65
+ else
66
+ instance_variable_set("@#{attr}", val)
67
+ end
68
+ end
69
+
70
+ def method_missing(meth, *args, &block)
71
+ if context.respond_to?(meth)
72
+ context.__send__(meth, *args, &block)
73
+ else
74
+ super
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,52 @@
1
+ module SousChef
2
+ module Resource
3
+ class Directory < Base
4
+ ACTIONS = %w[create delete]
5
+
6
+ def initialize(*args)
7
+ action :create
8
+ super
9
+ end
10
+
11
+ def path(path=nil)
12
+ set_or_return(:path, path) || name
13
+ end
14
+
15
+ def mode(mode=nil)
16
+ set_or_return(:mode, mode)
17
+ end
18
+
19
+ def action(action=nil)
20
+ set_or_return(:action, action && validate_action(action))
21
+ end
22
+
23
+ def to_script
24
+ @script ||= begin
25
+ setup
26
+ __send__(action)
27
+ [super, mode_command].compact.join("\n")
28
+ end
29
+ end
30
+
31
+ protected
32
+ def create
33
+ command %{mkdir -p #{escape_path(path)}}
34
+ end
35
+
36
+ def delete
37
+ command %{rmdir #{escape_path(path)}}
38
+ end
39
+
40
+ def validate_action(action)
41
+ return action if ACTIONS.include?(action.to_s)
42
+ raise ArgumentError, "Invalid action #{action}, only #{ACTIONS.join(', ')} allowed"
43
+ end
44
+
45
+ def mode_command
46
+ if mode
47
+ sprintf(%{chmod %04o %s}, mode, escape_path(path))
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,33 @@
1
+ module SousChef
2
+ module Resource
3
+ class Execute < Base
4
+ def initialize(context, name=nil, &block)
5
+ super
6
+
7
+ @cwd = nil
8
+ end
9
+
10
+ def to_script
11
+ @script ||= begin
12
+ prepend %{cd #{escape_path(@cwd)}} if @cwd
13
+ @commands = @commands.inject([]) do |result, line|
14
+ result + line.split("\n")
15
+ end
16
+ super
17
+ end
18
+ end
19
+
20
+ def cwd(dir=nil)
21
+ set_or_return(:cwd, dir)
22
+ end
23
+
24
+ def command(cmd)
25
+ @commands << cmd
26
+ end
27
+
28
+ def creates(path)
29
+ not_if %{test -e #{escape_path(path)}}
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,36 @@
1
+ module SousChef
2
+ module Resource
3
+ class File < Directory
4
+ def content(content=nil)
5
+ set_or_return(:content, content)
6
+ end
7
+
8
+ protected
9
+ def escaped_content
10
+ escape_string(content)
11
+ end
12
+
13
+ def create
14
+ not_if file_exist_command
15
+ command create_file_command
16
+ end
17
+
18
+ def delete
19
+ only_if file_exist_command
20
+ command "rm #{escape_path(path)}"
21
+ end
22
+
23
+ def file_exist_command
24
+ %{test -e #{escape_path(path)}}
25
+ end
26
+
27
+ def create_file_command
28
+ if content
29
+ %{echo '#{escaped_content}' > #{escape_path(path)}}
30
+ else
31
+ %{touch #{escape_path(path)}}
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,56 @@
1
+ require 'stringio'
2
+
3
+ module SousChef
4
+ module Resource
5
+ class Gemfile < File
6
+ def initialize(context, name, &block)
7
+ super
8
+ @gems = []
9
+ @sources = []
10
+ end
11
+
12
+ def gem(name, version=nil)
13
+ @gems << [name, version]
14
+ end
15
+
16
+ def source(url)
17
+ @sources << url
18
+ end
19
+
20
+ # override
21
+ def path(path=nil)
22
+ if path
23
+ super
24
+ else
25
+ gemfile = super
26
+ gemfile = "#{gemfile}/Gemfile" unless gemfile.split('/').last == "Gemfile"
27
+ gemfile
28
+ end
29
+ end
30
+
31
+ protected
32
+ # override
33
+ def content
34
+ result = StringIO.new
35
+ if @sources.any?
36
+ @sources.each { |url| result.puts %{source "#{url}"} }
37
+ result.puts
38
+ end
39
+
40
+ max_name_size = 0
41
+ @gems.each {|name,| max_name_size = [max_name_size, name.size].max}
42
+
43
+ @gems.sort_by {|name,| name.downcase}.each do |name, version|
44
+ if version.nil?
45
+ result.puts %{gem "#{name}"}
46
+ else
47
+ width = max_name_size + 3 # 2 quotes + comma
48
+ result.printf %{gem %-#{width}s "%s"\n}, %{"#{name}",}, version
49
+ end
50
+ end
51
+
52
+ result.string.strip
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,39 @@
1
+ module SousChef
2
+ module Resource
3
+ class Log < Base
4
+ def initialize(context, name=nil, &block)
5
+ super
6
+
7
+ @stdout = nil
8
+ @stderr = nil
9
+ end
10
+
11
+ def stdout(stdout=nil)
12
+ set_or_return(:stdout, stdout)
13
+ end
14
+
15
+ def stderr(stderr=nil)
16
+ set_or_return(:stderr, stderr)
17
+ end
18
+
19
+ def to_script
20
+ @script ||= begin
21
+ setup
22
+ unless @stdout || @stderr
23
+ @stdout, @stderr = name, "&1"
24
+ end
25
+ append exec_command(escape_path(@stdout), escape_path(@stderr))
26
+ super
27
+ end
28
+ end
29
+
30
+ protected
31
+ def exec_command(stdout, stderr)
32
+ args = []
33
+ args << "1>#{escape_path(stdout)}" if stdout
34
+ args << "2>#{escape_path(stderr)}" if stderr
35
+ "exec #{args.join(' ')}"
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,10 @@
1
+ module SousChef
2
+ module Resource
3
+ autoload :Base, 'sous_chef/resource/base'
4
+ autoload :Directory, 'sous_chef/resource/directory'
5
+ autoload :Execute, 'sous_chef/resource/execute'
6
+ autoload :File, 'sous_chef/resource/file'
7
+ autoload :Gemfile, 'sous_chef/resource/gemfile'
8
+ autoload :Log, 'sous_chef/resource/log'
9
+ end
10
+ end
data/lib/sous_chef.rb ADDED
@@ -0,0 +1,9 @@
1
+ module SousChef
2
+ autoload :Recipe, 'sous_chef/recipe'
3
+ autoload :Resource, 'sous_chef/resource'
4
+
5
+ def self.prep(*flags, &block)
6
+ recipe = Recipe.new(*flags, &block)
7
+ recipe.to_script
8
+ end
9
+ end
@@ -0,0 +1,102 @@
1
+ ruby_version = 'ruby-1.8.6'
2
+ rubygems_version = '1.3.5'
3
+ home_dir = '/home/sous_chef'
4
+
5
+ halt_on_failed_command
6
+
7
+ log do
8
+ stdout "/root/stdout.log"
9
+ stderr "/root/stderr.log"
10
+ end
11
+
12
+ file "/root/report.log" do
13
+ action :delete
14
+ end
15
+
16
+ def report(message)
17
+ command "echo '#{escape_string(message)}' >> /root/report.log"
18
+ end
19
+
20
+ file "/etc/config.yml" do
21
+ content node[:config].to_yaml
22
+ mode 0600
23
+ end
24
+
25
+ execute 'rvm' do
26
+ report "Installing ruby version manager"
27
+ creates "/usr/local/rvm/scripts/rvm"
28
+ command "gem install rvm && rvm-install"
29
+ end
30
+
31
+ execute "source rvm" do
32
+ command <<-EOS
33
+ RUBYOPT=""
34
+ source /usr/local/rvm/scripts/rvm
35
+ EOS
36
+ end
37
+
38
+ file "/etc/profile.d/rvm.sh" do
39
+ debugger
40
+ echo "Setting up rvm source"
41
+ content <<-EOS
42
+ # rvm configuration
43
+ RUBYOPT=""
44
+ if [[ -s /usr/local/rvm/scripts/rvm ]] ; then source /usr/local/rvm/scripts/rvm ; fi
45
+ EOS
46
+ end
47
+
48
+ echo 'installing ruby'
49
+
50
+ execute 'install ruby' do
51
+ command %{rvm install #{ruby_version}}
52
+ not_if "rvm list | grep #{ruby_version}"
53
+ end
54
+
55
+ execute 'use ruby' do
56
+ command "rvm use #{ruby_version}"
57
+ end
58
+
59
+ execute 'update rubygems' do
60
+ report "upgrading rubygems"
61
+ command <<-EOS
62
+ gem uninstall rubygems-update
63
+ gem install rubygems-update -v #{rubygems_version} --no-ri --no-rdoc
64
+ update_rubygems
65
+ gem uninstall rubygems-update
66
+ EOS
67
+ not_if "gem -v | grep #{rubygems_version}"
68
+ end
69
+
70
+ directory home_dir do
71
+ mode 0755
72
+ end
73
+
74
+ gemfile home_dir do
75
+ source "http://gemcutter.org/"
76
+
77
+ gem 'chef'
78
+ gem 'dbi', '0.4.3'
79
+ gem 'dbd-mysql', '0.4.3'
80
+ gem 'open4', '0.9.6'
81
+ end
82
+
83
+ execute 'install bundler' do
84
+ command "gem install bundler --no-ri --no-rdoc"
85
+ end
86
+
87
+ execute 'bundle gems' do
88
+ cwd home_dir
89
+ command "gem bundle"
90
+ end
91
+
92
+ file "/etc/profile.d/bin-path.sh" do
93
+ echo "Setting up bin-path"
94
+ content <<-EOS
95
+ # bundled gem bin path configuration
96
+ export PATH=#{home_dir}/bin:\$PATH
97
+ EOS
98
+ end
99
+
100
+ execute 'run chef' do
101
+ command "nohup #{home_dir}/bin/chef #{node[:chef_args]} &"
102
+ end