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 +42 -15
- data/lib/chap/cli.rb +16 -0
- data/lib/chap/runner.rb +19 -1
- data/lib/chap/task.rb +7 -1
- data/lib/chap/version.rb +1 -1
- data/spec/lib/chap_spec.rb +67 -26
- data/spec/spec_helper.rb +46 -29
- metadata +2 -2
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
|
-
|
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
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
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
|
-
|
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>
|
data/lib/chap/cli.rb
CHANGED
@@ -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
|
data/lib/chap/runner.rb
CHANGED
@@ -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)
|
data/lib/chap/task.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/chap/version.rb
CHANGED
data/spec/lib/chap_spec.rb
CHANGED
@@ -2,70 +2,111 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Chap do
|
4
4
|
before(:each) do
|
5
|
-
|
6
|
-
|
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 => "#{
|
16
|
+
:config => "#{system_root}/etc/chef/chap.yml"
|
16
17
|
)
|
17
18
|
timestamp = @chap.config.timestamp
|
18
|
-
@release_path = "#{
|
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?("#{
|
24
|
+
File.exist?("#{system_root}/data/chapdemo/shared").should be_true
|
24
25
|
File.exist?(@release_path).should be_true
|
25
|
-
File.symlink?("#{
|
26
|
-
releases = Dir.glob("#{
|
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 #{
|
39
|
-
releases = Dir.glob("#{
|
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 = "#{
|
37
|
+
release_path = "#{system_root}/data/chapdemo/releases/#{timestamp}"
|
42
38
|
|
43
|
-
File.exist?("#{
|
39
|
+
File.exist?("#{system_root}/data/chapdemo/shared").should be_true
|
44
40
|
File.exist?(release_path).should be_true
|
45
|
-
File.symlink?("#{
|
46
|
-
releases = Dir.glob("#{
|
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 #{
|
52
|
-
releases = Dir.glob("#{
|
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 = "#{
|
55
|
-
current_path = "#{
|
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 #{
|
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 #{
|
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
|
data/spec/spec_helper.rb
CHANGED
@@ -7,37 +7,54 @@ $root = File.expand_path('../../', __FILE__)
|
|
7
7
|
|
8
8
|
require "#{$root}/lib/chap"
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
#
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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.
|
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-
|
12
|
+
date: 2013-02-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|