baker 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -1,18 +1,59 @@
1
- runchef
1
+ Baker
2
2
  =======
3
3
 
4
- A Simple Way to Run Chef recipes
4
+ A simple way to run chef recipes on one server.
5
5
 
6
6
  Install
7
7
  -------
8
8
 
9
9
  <pre>
10
- sudo gem install baker --source http://gemcutter.org --no-ri --no-rdoc
10
+ gem install baker --source http://gemcutter.org --no-ri --no-rdoc # sudo if you need to
11
11
  </pre>
12
12
 
13
+ Prerequisite
14
+ -------
15
+
16
+ You need to set up sshkeys on the server so you can ssh into the box without passwords.
17
+
18
+ On the server:
19
+
20
+ You'll need to make sure to have chef installed.
21
+
22
+ On the client:
23
+
24
+ First you need to be in a cookbooks project. Here's an example of a mininum cookbooks project:
25
+
26
+ ├── config
27
+ │ └── baker
28
+ │ ├── node.json
29
+ │ └── solo.rb
30
+ └── cookbooks
31
+ ├── example_recipe1
32
+ │ └── recipes
33
+ │ └── default.rb
34
+ └── example_recipe2
35
+ └── recipes
36
+ └── default.rb
37
+
38
+ config/baker/node.json and config/baker/solo.rb are important. These are the configurations that get passed to the chef run that will tell it which recipes to run.
39
+
40
+ You need configure solo.rb to have this:
41
+
42
+ solo.rb:
43
+ file_cache_path "/tmp/baker"
44
+ cookbook_path "/tmp/baker/recipes/cookbooks"
45
+
46
+ node.json will determine what recipes you'll run:
47
+
48
+ config/baker/node.json:
49
+
50
+
13
51
  Usage
14
52
  -------
15
53
 
54
+ Once all that is set up, you can run baker and that will upload the recipes to the server and run them.
55
+ Errors are logged to /var/log/baker-chef-server.log and /var/log/baker-chef-client.log.
56
+
16
57
  <pre>
17
- baker <server>
58
+ bake <server>
18
59
  </pre>
data/TODO ADDED
@@ -0,0 +1,28 @@
1
+ * if there is an error on the server chef-solo run, how can I notify bake command on the client.
2
+ * uploading baker-client.log to server after done or on at_exit, take logger from ey-cap
3
+
4
+ #
5
+ def execute
6
+ command = "#{@opts[:chef_bin]} -j /etc/chef/dna.json -c #{chef_config} -r \"#{@recipes_url}\" > #{@chef_log} 2>&1"
7
+ @logger.debug "Running: #{command}"
8
+ if system(command)
9
+ @logger.info "Running telinit"
10
+ system("telinit q")
11
+ @logger.info "Finished #{@name} chef run"
12
+ else
13
+ @logger.error("#{@name} chef run failed. Reporting error")
14
+ raise DeployError, "#{@name} chef run failed"
15
+ end
16
+ ensure
17
+ if File.exists?(@chef_log)
18
+ FileUtils.ln_sf(@chef_log, "/var/log/chef.#{@name}.log")
19
+ end
20
+ end
21
+
22
+ ##################################################################
23
+
24
+ @enzyme_log = Logger.new("/var/log/enzyme.log")
25
+ @stderr_log = Logger.new($stderr)
26
+
27
+ @stderr_log.send(level, message)
28
+ @enzyme_log.send(level, message)
data/baker.log ADDED
@@ -0,0 +1 @@
1
+ # Logfile created on Sun Jul 11 19:37:09 -0700 2010 by logger.rb/22283
data/bin/{baker → bake} RENAMED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require File.join(File.dirname(__FILE__),'..','lib','baker')
3
+ require File.join(File.expand_path('../../lib/baker', __FILE__))
4
4
 
5
5
  server = ARGV[0]
6
6
  if server.nil? or server == ""
7
7
  puts "Usage: baker <server-name>"
8
8
  exit 1
9
9
  end
10
- Baker.run(server)
10
+ Baker.run(:host => server)
data/gemspec.rb CHANGED
@@ -7,12 +7,12 @@ GEM_SPEC = Gem::Specification.new do |s|
7
7
  s.homepage = "http://github.com/tongueroo/#{GEM_NAME}"
8
8
  s.summary = "A simple way to run chef recipes"
9
9
  # == CONFIGURE ==
10
- s.executables += [GEM_NAME]
10
+ s.executables += ["bake"]
11
11
  s.extra_rdoc_files = [ "README.markdown" ]
12
12
  s.files = GEM_FILES.to_a
13
13
  s.has_rdoc = false
14
14
  s.name = GEM_NAME
15
15
  s.platform = Gem::Platform::RUBY
16
16
  s.require_path = "lib"
17
- s.version = "0.1.0"
17
+ s.version = "0.1.1"
18
18
  end
data/lib/baker.rb CHANGED
@@ -1,93 +1,133 @@
1
1
  # TODO: upload config files: dna.json and solo.rb
2
2
 
3
+ require 'rubygems'
3
4
  require 'net/ssh'
4
5
  require 'pp'
5
6
 
7
+ class NotCookbookProject < RuntimeError; end
8
+
6
9
  class Baker
7
- def self.run(host)
8
- new(host)
10
+ def self.run(options)
11
+ @baker = Baker.new(options)
12
+ @baker.run
13
+ end
14
+
15
+ def initialize(options)
16
+ @host = options[:host] || raise("need to set host")
17
+ @user = options[:user]
18
+ @root = options[:root] || Dir.pwd
19
+ set_logger
20
+ end
21
+
22
+ def set_logger
23
+ @logger = if File.exist?(@root+"/log")
24
+ Logger.new(@root+"/log/baker.log")
25
+ else
26
+ Logger.new(@root+"/baker.log")
27
+ end
9
28
  end
10
-
11
- def initialize(host, user = nil)
12
- log "start running chef recipes on #{host}"
13
- @debug = true
14
- @host = host
15
- @user = user
29
+
30
+ def run
31
+ validate_cookbook_project
32
+ log "*** start running chef recipes on #{@host}"
16
33
  Net::SSH.start(@host, @user) do |ssh|
17
- check
18
- upload_chef_configs(ssh)
19
34
  upload_recipes(ssh)
20
35
  run_chef(ssh)
21
36
  end
22
- log "done running chef recipes on #{host}"
37
+ log "*** done running chef recipes on #{@host}, check /var/log/baker-chef-server.log"
23
38
  end
24
-
25
- def check
39
+
40
+ def validate_cookbook_project
26
41
  if !File.exist?('cookbooks')
27
- raise "not in chef cookbooks project need to be in one"
28
- end
29
- end
30
- def upload_chef_configs(ssh)
31
- log "uploading chef configs to #{@host}..."
32
- if !File.exist?("config/baker/dna.json") or !File.exist?("config/baker/solo.rb")
33
- raise "need to create a config/baker/dna.json and config/baker/solo.rb file, so it can be uploaded to the server that needs it"
42
+ raise NotCookbookProject.new("not in chef cookbooks project, @root is #{@root}")
34
43
  end
35
- bash_exec("tar czf chef-config.tgz config/baker")
36
- bash_exec("scp chef-config.tgz #{@host}:")
37
- ssh_exec(ssh, "rm -rf chef-config && tar -zxf chef-config.tgz && mv config/baker chef-config")
38
- ssh_exec(ssh, "rm -f chef-config.tgz")
39
- bash_exec("rm -f chef-config.tgz")
40
44
  end
45
+
41
46
  def upload_recipes(ssh)
42
- log "uploading chef recipes to #{@host}..."
43
- @file_cache_path = "/tmp/chef-solo"
44
- @recipes_path = "/tmp/chef-solo/recipes"
45
- # create
46
- bash_exec("tar czf recipes.tgz .")
47
- # upload
48
- bash_exec("scp recipes.tgz #{@host}:")
49
- # move
50
- ssh_exec(ssh, "rm -rf #{@recipes_path}")
51
- ssh_exec(ssh, "mkdir -p #{@recipes_path}")
52
- ssh_exec(ssh, "tar -zxf recipes.tgz -C #{@recipes_path}")
53
- bash_exec("rm recipes.tgz")
47
+ if !File.exist?("config/dna.json") or !File.exist?("config/solo.rb")
48
+ raise "need to create a config/dna.json and config/solo.rb file, so it can be uploaded to the server that needs it"
49
+ end
50
+
51
+ log "*** uploading chef recipes to #{@host}..."
52
+ @recipes_path = "/tmp/baker/recipes"
53
+ @tarball = "#{File.dirname(@recipes_path)}/recipes.tgz"
54
+ # create tarball
55
+ local_cmd("tar czf /tmp/recipes.tgz config cookbooks")
56
+ # upload to /tmp/baker/recipes
57
+ remote_cmd(ssh, "if [ -d '#{@recipes_path}' ] ; then rm -rf #{@recipes_path}; fi") # cleanup from before
58
+ remote_cmd(ssh, "if [ ! -d '#{@recipes_path}' ] ; then mkdir -p #{@recipes_path}; fi")
59
+ local_cmd("scp /tmp/recipes.tgz #{@host}:#{@tarball}")
60
+ # not using -C flag changes /root folder owner!!! and screws up ssh access
61
+ remote_cmd(ssh, "tar -zxf #{@tarball} -C #{@recipes_path}")
62
+ # # cleanup both remote and local
63
+ remote_cmd(ssh, "rm -f /tmp/baker/recipes.tgz")
64
+ local_cmd("rm -f /tmp/recipes.tgz")
54
65
  end
55
-
66
+
56
67
  def run_chef(ssh)
57
- log "running chef recipes on #{@host}..."
58
- chef_cmd = "chef-solo -c ~/chef-config/solo.rb -j ~/chef-config/dna.json"
59
- log "chef_cmd : #{chef_cmd}"
60
- ssh_exec(ssh, chef_cmd)
61
- end
62
-
63
- private
64
- def log(msg)
65
- puts msg
68
+ log "*** running chef recipes on #{@host}..."
69
+ chef_cmd = "chef-solo -c /tmp/baker/recipes/config/solo.rb -j /tmp/baker/recipes/config/dna.json > /var/log/baker-chef-server.log 2>&1"
70
+ log "CHEF_CMD : #{chef_cmd}"
71
+ remote_cmd(ssh, chef_cmd)
66
72
  end
67
- def debug(msg)
68
- puts msg if @debug
69
- end
70
- def bash_exec(command)
71
- `#{command}`
72
- end
73
- def ssh_exec(ssh, command)
74
73
 
75
- unless ARGV.empty?
76
- debug "Executing command: #{command}"
74
+ private
75
+ def log(msg)
76
+ puts(msg)
77
+ @logger.info(msg)
77
78
  end
78
79
 
79
- stdout = ""
80
- ssh.exec!(command) do |channel, stream, data|
81
- stdout << data if stream == :stdout
80
+ def local_cmd(command)
81
+ puts "local cmd: #{command}"
82
+ `#{command}`
82
83
  end
83
- output = stdout
84
-
85
- if output and @debug
86
- output = output.split("\n").join("\n ")
87
- debug " ssh output: #{output}"
84
+
85
+ def remote_cmd(ssh, command)
86
+ puts "remote cmd: #{command}"
87
+ stdout = ""
88
+ ssh.exec!(command) do |channel, stream, data|
89
+ stdout << data if stream == :stdout
90
+ end
91
+ output = stdout
92
+
93
+ if output
94
+ output = output.split("\n").join("\n ")
95
+ puts "remote output: #{output}"
96
+ end
97
+
98
+ output
88
99
  end
89
100
 
90
- output
91
- end
92
- end
93
-
101
+ # TODO: mess with this later so try to catch the stderr when chef-solo run fails
102
+ # def remote_cmd(ssh, command)
103
+ # puts "remote cmd: #{command}"
104
+ # ssh.open_channel do |channel|
105
+ # channel.exec(command) do |ch, success|
106
+ # unless success
107
+ # abort "FAILED: couldn't execute command (ssh.channel.exec failure) #{command}"
108
+ # end
109
+ # # stdout
110
+ # channel.on_data do |ch, data| # stdout
111
+ # print data
112
+ # end
113
+ # # stderr
114
+ # channel.on_extended_data do |ch, type, data|
115
+ # next unless type == 1 # only handle stderr
116
+ # $stderr.print data
117
+ # end
118
+ # channel.on_request("exit-status") do |ch, data|
119
+ # exit_code = data.read_long
120
+ # if exit_code > 0
121
+ # puts "ERROR: exit code #{exit_code}"
122
+ # else
123
+ # puts "success"
124
+ # end
125
+ # end
126
+ # channel.on_request("exit-signal") do |ch, data|
127
+ # puts "SIGNAL: #{data.read_long}"
128
+ # end
129
+ # end
130
+ # end
131
+ # end
132
+
133
+ end
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ require 'test/spec'
3
+ require 'mocha'
4
+ require File.dirname(__FILE__)+"/../lib/baker"
5
+
6
+ context "Baker" do
7
+ specify "should check that its running from a cookbooks project" do
8
+ @root = File.dirname(__FILE__)+"/fixtures/cookbooks-empty"
9
+ @baker = Baker.new(:host => "test_server", :root => @root)
10
+ File.directory?(@root+"/cookbooks").should == false
11
+ should.raise(NotCookbookProject) { @baker.run }
12
+ end
13
+
14
+ # specify "should upload baker configs and recipes" do
15
+ # Net::SSH.stubs(:configuration_for).returns({})
16
+ # Net::SSH.expects(:start).with(@server.host, "default-user", @options).returns(success = Object.new)
17
+ #
18
+ # Net::SSH.start(@host, @user) do |ssh|
19
+ # upload_chef_configs(ssh)
20
+ # upload_recipes(ssh)
21
+ # run_chef(ssh)
22
+ # end
23
+ #
24
+ # end
25
+ end
@@ -0,0 +1 @@
1
+ # Logfile created on Sun Jul 11 19:38:14 -0700 2010 by logger.rb/22283
@@ -0,0 +1,3 @@
1
+ # Logfile created on Tue Jan 11 22:54:01 -0800 2011 by logger.rb/22285
2
+ I, [2011-01-11T22:54:01.081234 #15321] INFO -- : start running chef recipes on usolo.loc
3
+ I, [2011-01-11T22:55:43.655179 #15337] INFO -- : start running chef recipes on usolo.loc
@@ -0,0 +1,16 @@
1
+ {
2
+ "ec2": false,
3
+ "user":"root",
4
+ "packages":[],
5
+ "gems":[],
6
+ "users":[],
7
+ "environment": {"name":"staging"},
8
+ "packages":{},
9
+ "gems_to_install":[
10
+ {"name": "sinatra", "version": "0.9.4"}
11
+ ],
12
+ "recipes":[
13
+ "example_recipe1",
14
+ "example_recipe1"
15
+ ]
16
+ }
@@ -0,0 +1,2 @@
1
+ file_cache_path "/tmp/chef-solo"
2
+ cookbook_path "/tmp/chef-solo/recipes/cookbooks"
@@ -0,0 +1 @@
1
+ Chef::Log.info("example recipe 1 ran")
@@ -0,0 +1 @@
1
+ Chef::Log.info("example recipe 2 ran")
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: baker
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 25
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
7
8
  - 1
8
- - 0
9
- version: 0.1.0
9
+ - 1
10
+ version: 0.1.1
10
11
  platform: ruby
11
12
  authors:
12
13
  - Tung Nguyen
@@ -14,24 +15,33 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-03-19 00:00:00 -07:00
18
+ date: 2011-01-12 00:00:00 -08:00
18
19
  default_executable:
19
20
  dependencies: []
20
21
 
21
22
  description:
22
23
  email: tongueroo@gmail.com
23
24
  executables:
24
- - baker
25
+ - bake
25
26
  extensions: []
26
27
 
27
28
  extra_rdoc_files:
28
29
  - README.markdown
29
30
  files:
30
- - bin/baker
31
+ - baker.log
32
+ - bin/bake
31
33
  - gemspec.rb
32
34
  - lib/baker.rb
33
35
  - Rakefile
34
36
  - README.markdown
37
+ - test/baker_test.rb
38
+ - test/fixtures/cookbooks-empty/baker.log
39
+ - test/fixtures/cookbooks-valid/baker.log
40
+ - test/fixtures/cookbooks-valid/config/baker/node.json
41
+ - test/fixtures/cookbooks-valid/config/baker/solo.rb
42
+ - test/fixtures/cookbooks-valid/cookbooks/example_recipe1/recipes/default.rb
43
+ - test/fixtures/cookbooks-valid/cookbooks/example_recipe2/recipes/default.rb
44
+ - TODO
35
45
  has_rdoc: true
36
46
  homepage: http://github.com/tongueroo/baker
37
47
  licenses: []
@@ -42,23 +52,27 @@ rdoc_options: []
42
52
  require_paths:
43
53
  - lib
44
54
  required_ruby_version: !ruby/object:Gem::Requirement
55
+ none: false
45
56
  requirements:
46
57
  - - ">="
47
58
  - !ruby/object:Gem::Version
59
+ hash: 3
48
60
  segments:
49
61
  - 0
50
62
  version: "0"
51
63
  required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
52
65
  requirements:
53
66
  - - ">="
54
67
  - !ruby/object:Gem::Version
68
+ hash: 3
55
69
  segments:
56
70
  - 0
57
71
  version: "0"
58
72
  requirements: []
59
73
 
60
74
  rubyforge_project:
61
- rubygems_version: 1.3.6
75
+ rubygems_version: 1.3.7
62
76
  signing_key:
63
77
  specification_version: 3
64
78
  summary: A simple way to run chef recipes