chap 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -19,17 +19,13 @@ The same command is called whether you're using chef or capistrano for deploymen
19
19
  $ gem install chap
20
20
  </pre>
21
21
 
22
- ## Usage
23
-
24
- The chap command is meant to be executed on the server which you want to deploy the code.
25
-
26
22
  ## Setup
27
23
 
28
24
  Chap requires 3 configuration files: chap.yml, chap.json and node.json.
29
25
 
30
26
  * chap.json: contains capistrano-like configuration settings.
31
27
  * node.json: is intended to be the same file that chef solo uses and contains instance specific information, like node[:instance_role].
32
- : chap.yml: The paths of the chap.json and node.json are configured in this file.
28
+ * chap.yml: The paths of the chap.json and node.json are configured in this file.
33
29
 
34
30
  Here are examples of the starter setup files that you can generate via:
35
31
 
@@ -60,6 +56,10 @@ $ cat /etc/chef/node.json
60
56
  }
61
57
  </pre>
62
58
 
59
+ ## Usage
60
+
61
+ The chap command is meant to be executed on the server which you want to deploy the code.
62
+
63
63
  ### Deploy sequence
64
64
 
65
65
  The deploy sequence is based on the sequence from the capistrano and chef deploy resource provider.
@@ -97,16 +97,14 @@ Chap loads up information from node.json because it needs the information for ho
97
97
  <pre>
98
98
  $ cat chap/restart
99
99
  #!/usr/bin/env ruby
100
- restart = case node[:instance_role]
101
- when 'app'
102
- "touch tmp/restart.txt"
103
- when 'resque'
104
- "rvmsudo bluepill restart resque"
105
- end
106
- run "cd #{current_path} && #{restart}"
100
+ if node[:instance_role] == 'app'
101
+ run "cd #{current_path} && touch tmp/restart.txt"
102
+ elsif node[:instance_role] == 'resque'
103
+ run "rvmsudo bluepill restart resque"
104
+ end
107
105
  </pre>
108
106
 
109
- ## Deploy Hooks
107
+ ### Deploy Hooks
110
108
 
111
109
  Define your deploy hooks in the chap folder of the project. There are 2 deploy hooks.
112
110
 
@@ -118,12 +116,32 @@ Deploy hooks get evaluated within the context of a chap deploy run and have some
118
116
  Special variables:
119
117
 
120
118
  * node - contains data from /etc/chef/node.json. Avaiable as mash.
121
- * chap - contains data from /etc/chef/chap.json and some special variables added by chap. Avaiable as mash. Special variables: release_path, current_path, shared_path, cached_path. The special variables are also available directly as methods.
119
+ * chap - contains data from /etc/chef/chap.json and some special variables added by chap. Avaiable as mash. Special variables: release_path, current_path, shared_path, cached_path, latest_release. The special variables are also available directly as methods.
122
120
 
123
121
  Special methods:
124
122
 
125
123
  * run - output the command to be ran and runs command.
126
124
  * log - log messages to [shared_path]/chap/chap.log.
125
+ * symlink_configs - useful as a chap/deploy hook. Symlinks any config files in [shared_path]/config/* over to [release_path]/config.
126
+ * with - used to prepend all commands with the run method in a block with another command. A example is provided below.
127
+
128
+
129
+ with example:
130
+
131
+ <pre>
132
+ with "cd #{release_path} && RAILS_ENV=#{node[:environment]} " do
133
+ run "rake do:something1"
134
+ run "rake do:something2"
135
+ end
136
+ </pre>
137
+
138
+ is the same as:
139
+
140
+ <pre>
141
+ run "cd #{release_path} && RAILS_ENV=#{node[:environment]} rake do:something1"
142
+ run "cd #{release_path} && RAILS_ENV=#{node[:environment]} rake do:something2"
143
+ </pre>
144
+
127
145
 
128
146
  ### Test deploy hooks
129
147
 
@@ -134,4 +152,13 @@ $ cap hook deploy
134
152
  $ cap hook restart
135
153
  </pre>
136
154
 
137
- This will test the hooks on the latest timestamp release at [deploy_to]/releases/[timestamp].
155
+ This will test the hooks on the latest timestamp release at [deploy_to]/releases/[timestamp].
156
+
157
+ ### Syncing restart phase
158
+
159
+ Some apps require that all the code be available on all the servers before a restart should happen on any of the servers. For example, if you're serving assets on the same server as your app code, you wan to make sure that the assets have been download on all servers before any of the servers start serving the new assets. To sync the retart phase you have to break out the capistano recipe so that it calls 2 chap command:
160
+
161
+ <pre>
162
+ $ chap deploy --stop-at-symlink
163
+ $ chap deploy --cont-at-symlink
164
+ </pre>
@@ -19,6 +19,8 @@ module Chap
19
19
  EOL
20
20
  method_option :quiet, :aliases => '-q', :type => :boolean, :desc => "Quiet commands"
21
21
  method_option :config, :aliases => '-c', :default => '/etc/chef/chap.yml', :desc => "chap.yml config to use"
22
+ method_option :stop_at_symlink, :type => :boolean, :desc => "Deploy code but stop right before the symlink"
23
+ method_option :cont_at_symlink, :type => :boolean, :desc => "Symlink and contine the deploy"
22
24
  def deploy
23
25
  Chap::Task.deploy(options)
24
26
  end
@@ -36,5 +38,19 @@ module Chap
36
38
  def hook(name)
37
39
  Chap::Runner.new(options).test_hook(name)
38
40
  end
41
+
42
+ desc "symlink", "Symlink latest timestamp release to current"
43
+ long_desc <<-EOL
44
+ Example:
45
+
46
+ $ chap symlink
47
+
48
+ Useful for testing between testing deploy and restart hooks.
49
+ EOL
50
+ method_option :quiet, :aliases => '-q', :type => :boolean, :desc => "Quiet commands"
51
+ method_option :config, :aliases => '-c', :default => '/etc/chef/chap.yml', :desc => "chap.yml config to use"
52
+ def symlink
53
+ Chap::Runner.new(options).symlink
54
+ end
39
55
  end
40
56
  end
@@ -9,16 +9,30 @@ module Chap
9
9
  end
10
10
 
11
11
  def deploy
12
+ deploy_to_symlink
13
+ deploy_from_symlink
14
+ end
15
+
16
+ def deploy_to_symlink
12
17
  setup
13
18
  strategy.deploy
14
19
  symlink_shared
15
20
  rm_rvmrc
16
21
  hook(:deploy)
22
+ end
23
+
24
+ def deploy_from_symlink(use_previous=false)
25
+ use_previous_timestamp if use_previous
17
26
  symlink_current
18
27
  hook(:restart)
19
28
  cleanup
20
29
  end
21
30
 
31
+ def symlink
32
+ use_previous_timestamp
33
+ symlink_current
34
+ end
35
+
22
36
  def setup
23
37
  user = config.chap[:user] || ENV['USER']
24
38
  group = config.chap[:group]
@@ -125,9 +139,13 @@ module Chap
125
139
  end
126
140
 
127
141
  def test_hook(name)
142
+ use_previous_timestamp
143
+ hook(name)
144
+ end
145
+
146
+ def use_previous_timestamp
128
147
  timestamp = File.basename(latest_release)
129
148
  config.override_timestamp(timestamp)
130
- hook(name)
131
149
  end
132
150
 
133
151
  def hook(name)
@@ -22,7 +22,13 @@ module Chap
22
22
 
23
23
  def self.deploy(options)
24
24
  runner = options.empty? ? Runner.new : Runner.new(options)
25
- runner.deploy
25
+ if options[:stop_at_symlink]
26
+ runner.deploy_to_symlink
27
+ elsif options[:cont_at_symlink]
28
+ runner.deploy_from_symlink(true)
29
+ else
30
+ runner.deploy
31
+ end
26
32
  end
27
33
  end
28
34
  end
@@ -1,3 +1,3 @@
1
1
  module Chap
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.7"
3
3
  end
@@ -2,70 +2,111 @@ require 'spec_helper'
2
2
 
3
3
  describe Chap do
4
4
  before(:each) do
5
- @root = File.expand_path("../../../", __FILE__)
6
- @system_root = "#{@root}/spec/fixtures/system_root"
5
+ setup
6
+ end
7
+
8
+ after(:each) do
9
+ FileUtils.rm_rf(system_root)
7
10
  end
8
11
 
9
12
  describe "internal code deploy" do
10
13
  before(:each) do
11
- setup_files
12
-
13
14
  @chap = Chap::Runner.new(
14
15
  :quiet => true,
15
- :config => "#{@system_root}/etc/chef/chap.yml"
16
+ :config => "#{system_root}/etc/chef/chap.yml"
16
17
  )
17
18
  timestamp = @chap.config.timestamp
18
- @release_path = "#{@system_root}/data/chapdemo/releases/#{timestamp}"
19
+ @release_path = "#{system_root}/data/chapdemo/releases/#{timestamp}"
19
20
  end
20
21
 
21
22
  it "should deploy code" do
22
23
  @chap.deploy
23
- File.exist?("#{@system_root}/data/chapdemo/shared").should be_true
24
+ File.exist?("#{system_root}/data/chapdemo/shared").should be_true
24
25
  File.exist?(@release_path).should be_true
25
- File.symlink?("#{@system_root}/data/chapdemo/current").should be_true
26
- releases = Dir.glob("#{@system_root}/data/chapdemo/releases/*").size
26
+ File.symlink?("#{system_root}/data/chapdemo/current").should be_true
27
+ releases = Dir.glob("#{system_root}/data/chapdemo/releases/*").size
27
28
  releases.should >= 1
28
- releases.should <= 2 # test the keep option
29
29
  end
30
30
  end
31
31
 
32
32
  describe "cli deploy" do
33
- before(:each) do
34
- setup_files
35
- end
36
-
37
33
  it "should deploy code via command line" do
38
- system("cd #{@root} && ./bin/chap deploy -q -c #{@system_root}/etc/chef/chap.yml")
39
- releases = Dir.glob("#{@system_root}/data/chapdemo/releases/*").sort
34
+ system("cd #{root} && ./bin/chap deploy -q -c #{system_root}/etc/chef/chap.yml")
35
+ releases = Dir.glob("#{system_root}/data/chapdemo/releases/*").sort
40
36
  timestamp = releases.last.split('/').last
41
- release_path = "#{@system_root}/data/chapdemo/releases/#{timestamp}"
37
+ release_path = "#{system_root}/data/chapdemo/releases/#{timestamp}"
42
38
 
43
- File.exist?("#{@system_root}/data/chapdemo/shared").should be_true
39
+ File.exist?("#{system_root}/data/chapdemo/shared").should be_true
44
40
  File.exist?(release_path).should be_true
45
- File.symlink?("#{@system_root}/data/chapdemo/current").should be_true
46
- releases = Dir.glob("#{@system_root}/data/chapdemo/releases/*").size
41
+ File.symlink?("#{system_root}/data/chapdemo/current").should be_true
42
+ releases = Dir.glob("#{system_root}/data/chapdemo/releases/*").size
47
43
  releases.should >= 1
48
44
  end
49
45
 
50
46
  it "should deploy code and test hook" do
51
- system("cd #{@root} && ./bin/chap deploy -q -c #{@system_root}/etc/chef/chap.yml")
52
- releases = Dir.glob("#{@system_root}/data/chapdemo/releases/*").sort
47
+ system("cd #{root} && ./bin/chap deploy -q -c #{system_root}/etc/chef/chap.yml")
48
+ releases = Dir.glob("#{system_root}/data/chapdemo/releases/*").sort
53
49
  timestamp = releases.last.split('/').last
54
- release_path = "#{@system_root}/data/chapdemo/releases/#{timestamp}"
55
- current_path = "#{@system_root}/data/chapdemo/current"
50
+ release_path = "#{system_root}/data/chapdemo/releases/#{timestamp}"
51
+ current_path = "#{system_root}/data/chapdemo/current"
56
52
  File.exist?(release_path).should be_true
57
53
  File.exist?("#{release_path}/deploy.txt").should be_true
58
54
 
59
55
  # test hook deploy
60
56
  FileUtils.rm_f("#{release_path}/deploy.txt")
61
57
  File.exist?("#{release_path}/deploy.txt").should be_false
62
- system("cd #{@root} && ./bin/chap hook deploy -q -c #{@system_root}/etc/chef/chap.yml")
58
+ system("cd #{root} && ./bin/chap hook deploy -q -c #{system_root}/etc/chef/chap.yml")
63
59
  File.exist?("#{release_path}/deploy.txt").should be_true
64
60
 
65
61
  # test hook restart
66
62
  FileUtils.rm_f("#{current_path}/restart.txt")
67
- system("cd #{@root} && ./bin/chap hook restart -q -c #{@system_root}/etc/chef/chap.yml")
63
+ system("cd #{root} && ./bin/chap hook restart -q -c #{system_root}/etc/chef/chap.yml")
68
64
  File.exist?("#{current_path}/restart.txt").should be_true
69
65
  end
66
+
67
+ it "should deploy code stopping and continuing at symlink" do
68
+ system("cd #{root} && ./bin/chap deploy --stop-at-symlink -q -c #{system_root}/etc/chef/chap.yml")
69
+ releases = Dir.glob("#{system_root}/data/chapdemo/releases/*").sort
70
+ timestamp = releases.last.split('/').last
71
+ release_path = "#{system_root}/data/chapdemo/releases/#{timestamp}"
72
+ current_path = "#{system_root}/data/chapdemo/current"
73
+ File.exist?(release_path).should be_true
74
+ # no current symlink
75
+ File.exist?(current_path).should_not be_true
76
+ releases = Dir.glob("#{system_root}/data/chapdemo/releases/*").size
77
+ releases.should == 1
78
+ sleep 2 if tier(2)
79
+ # continue deploy
80
+ system("cd #{root} && ./bin/chap deploy --cont-at-symlink -q -c #{system_root}/etc/chef/chap.yml")
81
+ link = File.readlink(current_path)
82
+ link.should == release_path
83
+ end
84
+
85
+ it "should symlink previous release to current" do
86
+ system("cd #{root} && ./bin/chap deploy --stop-at-symlink -q -c #{system_root}/etc/chef/chap.yml")
87
+ releases = Dir.glob("#{system_root}/data/chapdemo/releases/*").sort
88
+ timestamp = releases.last.split('/').last
89
+ release_path = "#{system_root}/data/chapdemo/releases/#{timestamp}"
90
+ current_path = "#{system_root}/data/chapdemo/current"
91
+ File.exist?(release_path).should be_true
92
+ # no current symlink
93
+ File.exist?(current_path).should_not be_true
94
+ system("cd #{root} && ./bin/chap symlink -q -c #{system_root}/etc/chef/chap.yml")
95
+ File.exist?(current_path).should be_true
96
+ link = File.readlink(current_path)
97
+ link.should == release_path
98
+ # running twice should link to same symlink
99
+ system("cd #{root} && ./bin/chap symlink -q -c #{system_root}/etc/chef/chap.yml")
100
+ link = File.readlink(current_path)
101
+ link.should == release_path
102
+ end
103
+
104
+ it "should only respect keep option" do
105
+ system("cd #{root} && ./bin/chap deploy -q -c #{system_root}/etc/chef/chap.yml")
106
+ system("cd #{root} && ./bin/chap deploy -q -c #{system_root}/etc/chef/chap.yml")
107
+ system("cd #{root} && ./bin/chap deploy -q -c #{system_root}/etc/chef/chap.yml")
108
+ releases = Dir.glob("#{system_root}/data/chapdemo/releases/*").size
109
+ releases.should == 2 # test the keep option
110
+ end
70
111
  end
71
112
  end
@@ -7,37 +7,54 @@ $root = File.expand_path('../../', __FILE__)
7
7
 
8
8
  require "#{$root}/lib/chap"
9
9
 
10
- # use to overwrite the demo one created from examples/chap.json for testing
11
- def setup_files(options={})
12
- FileUtils.mkdir_p("#{@system_root}/etc/chef")
13
- system("cd #{@root} && ./bin/chap setup -q -f -o #{@system_root}/etc/chef")
14
- update_chap_yml(options)
15
- update_chap_json(options)
16
- end
10
+ module Helpers
11
+ attr_reader :root, :system_root
12
+ def setup
13
+ @root = File.expand_path("../../", __FILE__)
14
+ @system_root = "#{@root}/spec/fixtures/system_root"
15
+ create_chap_files
16
+ end
17
17
 
18
- # change system_root for chap.yml
19
- def update_chap_yml(options)
20
- path = "#{@system_root}/etc/chef/chap.yml"
21
- yaml = YAML.load(IO.read(path))
22
- yaml['chap'] = @system_root + yaml['chap']
23
- yaml['node'] = @system_root + yaml['node']
24
- File.open("#{@system_root}/etc/chef/chap.yml", "w") do |file|
25
- data = YAML.dump(yaml)
26
- file.write(data)
18
+ # use to overwrite the demo one created from examples/chap.json for testing
19
+ def create_chap_files(options={})
20
+ FileUtils.mkdir_p("#{@system_root}/etc/chef")
21
+ system("cd #{@root} && ./bin/chap setup -q -f -o #{@system_root}/etc/chef")
22
+ update_chap_yml(options)
23
+ update_chap_json(options)
24
+ end
25
+
26
+ # change system_root for chap.yml
27
+ def update_chap_yml(options)
28
+ path = "#{@system_root}/etc/chef/chap.yml"
29
+ yaml = YAML.load(IO.read(path))
30
+ yaml['chap'] = @system_root + yaml['chap']
31
+ yaml['node'] = @system_root + yaml['node']
32
+ File.open("#{@system_root}/etc/chef/chap.yml", "w") do |file|
33
+ data = YAML.dump(yaml)
34
+ file.write(data)
35
+ end
27
36
  end
28
- end
29
37
 
30
- # change system_root for chap.json
31
- def update_chap_json(options)
32
- chap = JSON.parse(IO.read("#{@system_root}/etc/chef/chap.json"))
33
- chap['deploy_to'] = @system_root + chap['deploy_to']
34
- chap['strategy'] = ENV['TIER'] == '2' ? "checkout" : "copy"
35
- chap['source'] = @root + "/spec/fixtures/chapdemo"
36
- chap['keep'] = 2
37
- chap['user'] = ENV['USER']
38
- chap['group'] = nil
39
- File.open("#{@system_root}/etc/chef/chap.json", "w") do |file|
40
- json = JSON.pretty_generate(chap)
41
- file.write(json)
38
+ # change system_root for chap.json
39
+ def update_chap_json(options)
40
+ chap = JSON.parse(IO.read("#{@system_root}/etc/chef/chap.json"))
41
+ chap['deploy_to'] = @system_root + chap['deploy_to']
42
+ chap['strategy'] = ENV['TIER'] == '2' ? "checkout" : "copy"
43
+ chap['source'] = @root + "/spec/fixtures/chapdemo"
44
+ chap['keep'] = 2
45
+ chap['user'] = ENV['USER']
46
+ chap['group'] = nil
47
+ File.open("#{@system_root}/etc/chef/chap.json", "w") do |file|
48
+ json = JSON.pretty_generate(chap)
49
+ file.write(json)
50
+ end
42
51
  end
52
+
53
+ def tier(level)
54
+ ENV['TIER'] == level.to_s
55
+ end
56
+ end
57
+
58
+ RSpec.configure do |c|
59
+ c.include Helpers
43
60
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-16 00:00:00.000000000 Z
12
+ date: 2013-02-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake