chef_cap 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +37 -0
- data/LICENSE +19 -0
- data/README.rdoc +49 -0
- data/Rakefile +10 -0
- data/chef_cap.gemspec +21 -0
- data/fixtures/parser.json +3 -0
- data/fixtures/ssh_deploy_key +1 -0
- data/fixtures/ssh_public_key +1 -0
- data/init.rb +1 -0
- data/lib/chef_cap/tasks.rb +18 -0
- data/lib/chef_cap/version.rb +3 -0
- data/lib/chef_cap.rb +14 -0
- data/lib/generators/chef_cap/install_generator.rb +14 -0
- data/lib/generators/chef_cap/templates/Capfile +18 -0
- data/lib/generators/chef_cap/templates/chef/cookbooks/gems/recipes/default.rb +11 -0
- data/lib/generators/chef_cap/templates/chef/node.json +50 -0
- data/recipes/chef_cap.rb +196 -0
- data/recipes/cook.rb +7 -0
- data/recipes/lib/chef_cap_configuration.rb +19 -0
- data/recipes/lib/chef_cap_helper.rb +77 -0
- data/recipes/lib/chef_cap_initializer.rb +6 -0
- data/recipes/lib/chef_dna_parser.rb +36 -0
- data/recipes/rvm_bootstrap.rb +34 -0
- data/recipes/unset_default_deploy.rb +44 -0
- data/spec/chef_cap_configuration_spec.rb +27 -0
- data/spec/chef_cap_helper_spec.rb +126 -0
- data/spec/chef_cap_mock_cap.rb +267 -0
- data/spec/chef_cap_spec.rb +808 -0
- data/spec/chef_dna_parser_spec.rb +29 -0
- data/spec/spec_helper.rb +45 -0
- metadata +117 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
depend :remote, :command, "rvm"
|
2
|
+
depend :remote, :command, "chef-solo"
|
3
|
+
|
4
|
+
before "chef:setup", "rvm:bootstrap"
|
5
|
+
|
6
|
+
namespace :rvm do
|
7
|
+
desc "Create a standalone rvm installation with a default ruby to use with chef-solo"
|
8
|
+
task :bootstrap do
|
9
|
+
rvm_standup_script = <<-SH
|
10
|
+
#!/bin/bash
|
11
|
+
RVM_URL="http://rvm.beginrescueend.com/releases/rvm-install-latest"
|
12
|
+
export PATH=$PATH:/usr/local/bin
|
13
|
+
HAVE_RVM_ALREADY=`which rvm 2>/dev/null`
|
14
|
+
if [ ! -z $HAVE_RVM_ALREADY ]; then
|
15
|
+
echo "Looks like RVM is already on this machine. Skipping."
|
16
|
+
exit 0
|
17
|
+
fi
|
18
|
+
|
19
|
+
HAVE_CURL=`which curl 2>/dev/null`
|
20
|
+
if [ ! -z $HAVE_CURL ]; then
|
21
|
+
RVM_TEMP_FILE=`mktemp /tmp/rvm_bootstrap.XXXXXX`
|
22
|
+
curl $RVM_URL > $RVM_TEMP_FILE
|
23
|
+
chmod u+rx $RVM_TEMP_FILE
|
24
|
+
sh $RVM_TEMP_FILE
|
25
|
+
rm -f $RVM_TEMP_FILE
|
26
|
+
else
|
27
|
+
echo "FATAL ERROR: I have no idea how to download RVM without curl!"
|
28
|
+
exit 1
|
29
|
+
fi
|
30
|
+
SH
|
31
|
+
put rvm_standup_script, "/tmp/chef-cap-#{rails_env}-rvm-standup.sh", :mode => "0700"
|
32
|
+
sudo "/tmp/chef-cap-#{rails_env}-rvm-standup.sh"
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
namespace :deploy do
|
2
|
+
task :default do
|
3
|
+
chef.deploy
|
4
|
+
end
|
5
|
+
|
6
|
+
task :long do
|
7
|
+
# do nothing
|
8
|
+
end
|
9
|
+
|
10
|
+
task :update_code do
|
11
|
+
# do nothing
|
12
|
+
end
|
13
|
+
|
14
|
+
task :finalize_update do
|
15
|
+
# do nothing
|
16
|
+
end
|
17
|
+
|
18
|
+
task :symlink do
|
19
|
+
# do nothing
|
20
|
+
end
|
21
|
+
|
22
|
+
task :restart do
|
23
|
+
# do nothing
|
24
|
+
end
|
25
|
+
|
26
|
+
task :migrate do
|
27
|
+
# do nothing
|
28
|
+
end
|
29
|
+
|
30
|
+
task :start do
|
31
|
+
# do nothing
|
32
|
+
end
|
33
|
+
|
34
|
+
task :setup do
|
35
|
+
# do nothing
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Finally load deploy.rb if it exists and allow it to overwrite anything we've done here
|
40
|
+
unless File.exist?("config/deploy.rb")
|
41
|
+
def load(*args)
|
42
|
+
return true
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
|
2
|
+
|
3
|
+
describe ChefCapConfiguration do
|
4
|
+
describe ".set_repository_settings" do
|
5
|
+
it "should set repository values for a git repository" do
|
6
|
+
configuration = mock("Configuration")
|
7
|
+
configuration.stub!(:repository => "git@somegitrepo")
|
8
|
+
configuration.should_receive(:set).with(:scm, :git)
|
9
|
+
configuration.should_receive(:set).with(:git_enable_submodules, 1)
|
10
|
+
configuration.should_receive(:default_run_options).and_return({})
|
11
|
+
configuration.should_receive(:depend).with(:remote, :command, "git")
|
12
|
+
ChefCapConfiguration.configuration = configuration
|
13
|
+
|
14
|
+
ChefCapConfiguration.set_repository_settings
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should set repository values for an svn repository" do
|
18
|
+
configuration = mock("Configuration")
|
19
|
+
ChefCapConfiguration.configuration = configuration
|
20
|
+
configuration.stub!(:repository => "svn://somesvnrepo")
|
21
|
+
configuration.should_receive(:set).with(:scm, :svn)
|
22
|
+
configuration.should_receive(:depend).with(:remote, :command, "svn")
|
23
|
+
|
24
|
+
ChefCapConfiguration.set_repository_settings
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
|
2
|
+
|
3
|
+
describe ChefDnaParser do
|
4
|
+
|
5
|
+
describe ".recursive_merge" do
|
6
|
+
|
7
|
+
it "assigns a key value if the original has does not have the key" do
|
8
|
+
original_hash = {}
|
9
|
+
|
10
|
+
resulting_hash = ChefCapHelper.recursive_merge(original_hash, "newkey", "somevalue")
|
11
|
+
resulting_hash["newkey"].should == "somevalue"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "overwrites a value with the new value if there is a type mismatch" do
|
15
|
+
original_hash = {"key" => "value"}
|
16
|
+
|
17
|
+
resulting_hash = ChefCapHelper.recursive_merge(original_hash, "key", 1)
|
18
|
+
resulting_hash["key"].should == 1
|
19
|
+
resulting_hash = ChefCapHelper.recursive_merge(original_hash, "key", ["value"])
|
20
|
+
resulting_hash["key"].should == ["value"]
|
21
|
+
resulting_hash = ChefCapHelper.recursive_merge(original_hash, "key", {:key => :value})
|
22
|
+
resulting_hash["key"].should == {:key => :value}
|
23
|
+
end
|
24
|
+
|
25
|
+
it "uniquely merges the values of two arrays" do
|
26
|
+
original_hash = {"key" => ["original", "duplicate"]}
|
27
|
+
|
28
|
+
resulting_hash = ChefCapHelper.recursive_merge(original_hash, "key", ["duplicate", "newvalue"])
|
29
|
+
resulting_hash["key"].should =~ ["original", "duplicate", "newvalue"]
|
30
|
+
end
|
31
|
+
|
32
|
+
it "merges arrays of arrays" do
|
33
|
+
original_hash = {"key" => ["one", ["two", "three"]]}
|
34
|
+
|
35
|
+
resulting_hash = ChefCapHelper.recursive_merge(original_hash, "key", ["onepointfive", ["two", "three"]])
|
36
|
+
resulting_hash["key"].should =~ ["one", "onepointfive", ["two", "three"]]
|
37
|
+
end
|
38
|
+
|
39
|
+
it "merges keys and values of hashes" do
|
40
|
+
original_hash = {"key" => { "one" => "two" }}
|
41
|
+
|
42
|
+
resulting_hash = ChefCapHelper.recursive_merge(original_hash, "key", {"one" => "nottwo"})
|
43
|
+
resulting_hash["key"].should == { "one" => "nottwo" }
|
44
|
+
end
|
45
|
+
|
46
|
+
it "merge will overwrite hashes with arrays" do
|
47
|
+
original_hash = {"key" => {"array" => ["one"] }}
|
48
|
+
|
49
|
+
resulting_hash = ChefCapHelper.recursive_merge(original_hash, "key", {"array" => ["two", "three"]})
|
50
|
+
resulting_hash["key"].should == {"array" => ["two", "three"]}
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
describe ".roles_for_host" do
|
56
|
+
it "returns the list of roles that match for a given host" do
|
57
|
+
otherserver = stub("Server", :host => "somehostname")
|
58
|
+
matchserver = stub("Server", :host => "myhostname")
|
59
|
+
|
60
|
+
roles = { :matchrole => stub("Role", :servers => [otherserver, matchserver]),
|
61
|
+
:otherrole => stub("Role", :servers => [otherserver])}
|
62
|
+
ChefCapHelper.roles_for_host(roles, "myhostname").should == ["matchrole"]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe ".merge_roles_for_host" do
|
67
|
+
it "merges the run list if the host has multiple roles" do
|
68
|
+
roles_hash = {
|
69
|
+
"role1" => {
|
70
|
+
"something else" => "yes",
|
71
|
+
"run_list" => ["role1run"]
|
72
|
+
},
|
73
|
+
"role2" => {
|
74
|
+
"something else" => "yup",
|
75
|
+
"run_list" => ["role1run, role2run"]
|
76
|
+
},
|
77
|
+
"role3" => {
|
78
|
+
"something else" => "nope",
|
79
|
+
"run_list" => ["role3run"]
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
ChefCapHelper.merge_roles_for_host(roles_hash, ["role1", "role2"]).should == {"run_list"=>["role1run", "role1run, role2run"], "something else"=>"yup"}
|
84
|
+
end
|
85
|
+
|
86
|
+
it "returns empty hash" do
|
87
|
+
ChefCapHelper.merge_roles_for_host(nil, ["role1", "role2"]).should == {}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe ".rewrite_run_list_for_deploy" do
|
92
|
+
it "should move the deploy recipe to the end of the run list if it is specified" do
|
93
|
+
json_with_run_list = {
|
94
|
+
"run_list" => ["something", "something", "deployrecipe", "darkside"],
|
95
|
+
"deploy_recipe" => "deployrecipe"
|
96
|
+
}
|
97
|
+
|
98
|
+
ChefCapHelper.rewrite_run_list_for_deploy(json_with_run_list).should == ["something", "something", "darkside", "deployrecipe"]
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should do nothing if the run list does not contain the deploy recipe" do
|
102
|
+
json_with_run_list = {
|
103
|
+
"run_list" => ["something", "something", "darkside"],
|
104
|
+
"deploy_recipe" => "deployrecipe"
|
105
|
+
}
|
106
|
+
|
107
|
+
ChefCapHelper.rewrite_run_list_for_deploy(json_with_run_list).should == ["something", "something", "darkside"]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe ".intialize_primary_values" do
|
112
|
+
it "should make sure each server has a primary key with a value of empty array of it has nothing" do
|
113
|
+
array_of_servers_hash = [
|
114
|
+
{ "host" => "server1"}
|
115
|
+
]
|
116
|
+
ChefCapHelper.intialize_primary_values(array_of_servers_hash).should == [{"host" => "server1", "primary" => []}]
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should not touch the setting of a server if it already has a value" do
|
120
|
+
array_of_servers_hash = [
|
121
|
+
{ "host" => "server1", "primary" => ["one", "two"]}
|
122
|
+
]
|
123
|
+
ChefCapHelper.intialize_primary_values(array_of_servers_hash).should == array_of_servers_hash
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,267 @@
|
|
1
|
+
def parent
|
2
|
+
self
|
3
|
+
end
|
4
|
+
|
5
|
+
def namespace_objects
|
6
|
+
@namespace_objects ||= {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def default_environment
|
10
|
+
@default_environment ||= {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def find_servers(options = {})
|
14
|
+
servers = []
|
15
|
+
options[:roles].each do |role|
|
16
|
+
if @servers.has_key? role
|
17
|
+
@servers[role][:servers].each do |server|
|
18
|
+
servers << TestCapServer.new(server)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
servers
|
23
|
+
end
|
24
|
+
|
25
|
+
def task(name, *args)
|
26
|
+
@tasks ||= {}
|
27
|
+
@tasks[@curent_namespace ? "#{@curent_namespace}:#{name}" : name] = proc { yield }
|
28
|
+
if @curent_namespace
|
29
|
+
namespace_objects[@curent_namespace].instance_eval(<<-EOS)
|
30
|
+
def #{name}
|
31
|
+
configuration.cap_task["#{@current_namespace}:#{name}"]
|
32
|
+
end
|
33
|
+
EOS
|
34
|
+
end
|
35
|
+
@tasks
|
36
|
+
end
|
37
|
+
|
38
|
+
def cap_task
|
39
|
+
@tasks ||= {}
|
40
|
+
end
|
41
|
+
|
42
|
+
def role(name, hostname, options = {})
|
43
|
+
@servers ||= {}
|
44
|
+
@servers[name] ||= {:servers => []}
|
45
|
+
@servers[name][:servers] = @servers[name][:servers] << hostname
|
46
|
+
@servers[name][:primary] = hostname if options[:primary]
|
47
|
+
end
|
48
|
+
|
49
|
+
def roles
|
50
|
+
if JSON.parse(ChefDnaParser.test_dna)["roles"]
|
51
|
+
role_hash = {}
|
52
|
+
JSON.parse(ChefDnaParser.test_dna)["roles"].each_pair do |key, value|
|
53
|
+
role_klass = TestCapRole.new
|
54
|
+
|
55
|
+
role_klass.instance_eval(<<-EOS)
|
56
|
+
def name
|
57
|
+
#{key.inspect}
|
58
|
+
end
|
59
|
+
|
60
|
+
def servers
|
61
|
+
[]
|
62
|
+
end
|
63
|
+
EOS
|
64
|
+
if cap_role.any?
|
65
|
+
role_klass.instance_eval(<<-EOS)
|
66
|
+
def servers
|
67
|
+
role_servers = []
|
68
|
+
#{cap_role[key.to_sym][:servers].inspect}.each do |server_hostname|
|
69
|
+
host = TestCapServer.new
|
70
|
+
host.instance_eval(<<-EOH)
|
71
|
+
def host
|
72
|
+
\#{server_hostname.inspect}
|
73
|
+
end
|
74
|
+
EOH
|
75
|
+
role_servers << host
|
76
|
+
end
|
77
|
+
role_servers
|
78
|
+
end
|
79
|
+
EOS
|
80
|
+
end
|
81
|
+
role_hash[key] = role_klass
|
82
|
+
end
|
83
|
+
role_hash
|
84
|
+
else
|
85
|
+
[]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def cap_role
|
90
|
+
@servers ||= {}
|
91
|
+
end
|
92
|
+
|
93
|
+
def set(key, value)
|
94
|
+
key.to_s.gsub!(/\.|-/, '_')
|
95
|
+
self.instance_eval(<<-EOS)
|
96
|
+
def #{key}
|
97
|
+
#{value.inspect}
|
98
|
+
end
|
99
|
+
EOS
|
100
|
+
@variables ||= {}
|
101
|
+
@variables[key] = value
|
102
|
+
end
|
103
|
+
|
104
|
+
def cap_variable
|
105
|
+
@variables ||= {}
|
106
|
+
end
|
107
|
+
|
108
|
+
def depend(local_or_remote, dependency_type, path)
|
109
|
+
@dependencies ||= {}
|
110
|
+
@dependencies[path] = { local_or_remote => dependency_type }
|
111
|
+
end
|
112
|
+
|
113
|
+
def cap_depends
|
114
|
+
@dependencies ||= {}
|
115
|
+
end
|
116
|
+
|
117
|
+
def desc(message)
|
118
|
+
@task_description = message
|
119
|
+
end
|
120
|
+
|
121
|
+
def current_description
|
122
|
+
@task_description
|
123
|
+
end
|
124
|
+
|
125
|
+
def namespace(name, &block)
|
126
|
+
@namespaces ||= {}
|
127
|
+
@namespaces[name] = true
|
128
|
+
@curent_namespace = name
|
129
|
+
@namespace_objects ||= {}
|
130
|
+
namespace_objects[name] = TestCapMockNamespace.new
|
131
|
+
namespace_objects[name].configuration = self
|
132
|
+
self.instance_eval(<<-EOS)
|
133
|
+
def #{name}
|
134
|
+
namespace_objects['#{name}'.to_sym]
|
135
|
+
end
|
136
|
+
EOS
|
137
|
+
|
138
|
+
yield
|
139
|
+
ensure
|
140
|
+
@curent_namespace = nil
|
141
|
+
end
|
142
|
+
|
143
|
+
def cap_namespace
|
144
|
+
@namespaces ||= {}
|
145
|
+
end
|
146
|
+
|
147
|
+
def before(task_name, task_to_call, &block)
|
148
|
+
@before_callchain ||= {}
|
149
|
+
@before_callchain[task_name] ||= []
|
150
|
+
@before_callchain[task_name] << task_to_call
|
151
|
+
end
|
152
|
+
|
153
|
+
def cap_before
|
154
|
+
@before_callchain ||= {}
|
155
|
+
end
|
156
|
+
|
157
|
+
def after(task_name, task_to_call, &block)
|
158
|
+
@after_callchain ||= {}
|
159
|
+
@after_callchain[task_name] ||= []
|
160
|
+
@after_callchain[task_name] << task_to_call
|
161
|
+
end
|
162
|
+
|
163
|
+
def ssh_options
|
164
|
+
@ssh_options ||= {}
|
165
|
+
end
|
166
|
+
|
167
|
+
def cap_after
|
168
|
+
@after_callchain ||= {}
|
169
|
+
end
|
170
|
+
|
171
|
+
def cap_ssh_options
|
172
|
+
@ssh_options ||= {}
|
173
|
+
end
|
174
|
+
|
175
|
+
def default_run_options
|
176
|
+
@default_run_options ||= {}
|
177
|
+
end
|
178
|
+
|
179
|
+
def parallel_sessions
|
180
|
+
@parallel_sessions ||= []
|
181
|
+
end
|
182
|
+
|
183
|
+
def run(command, &block)
|
184
|
+
@commands_run ||= {}
|
185
|
+
if block_given?
|
186
|
+
@commands_run[command] = proc { yield }
|
187
|
+
else
|
188
|
+
@commands_run[command] = true
|
189
|
+
end
|
190
|
+
@commands_run
|
191
|
+
end
|
192
|
+
|
193
|
+
def cap_run
|
194
|
+
@commands_run ||= {}
|
195
|
+
end
|
196
|
+
|
197
|
+
def cap_servers
|
198
|
+
cap_role.each_value.map do |hash|
|
199
|
+
hash[:servers]
|
200
|
+
end.flatten.uniq
|
201
|
+
end
|
202
|
+
|
203
|
+
def parallel_mocks
|
204
|
+
@parallel_mocks ||= []
|
205
|
+
end
|
206
|
+
|
207
|
+
def parallel(options={})
|
208
|
+
cap_servers.each do |server|
|
209
|
+
session_klass = TestCapSession.new
|
210
|
+
session_klass.instance_eval do
|
211
|
+
def set_channel=(hash)
|
212
|
+
@channel = hash
|
213
|
+
end
|
214
|
+
|
215
|
+
def channel
|
216
|
+
@channel
|
217
|
+
end
|
218
|
+
|
219
|
+
def set_roles=(rolez)
|
220
|
+
@roles = rolez
|
221
|
+
end
|
222
|
+
|
223
|
+
def roles
|
224
|
+
@roles
|
225
|
+
end
|
226
|
+
|
227
|
+
def else(command, &block)
|
228
|
+
instance_exec(channel, &block)
|
229
|
+
end
|
230
|
+
|
231
|
+
def set_environment_settings=(something)
|
232
|
+
@env_settings = something
|
233
|
+
end
|
234
|
+
|
235
|
+
def environment_settings
|
236
|
+
@env_settings
|
237
|
+
end
|
238
|
+
|
239
|
+
def set(key, value)
|
240
|
+
@set ||= {}
|
241
|
+
@set[key] = value
|
242
|
+
end
|
243
|
+
|
244
|
+
def things_that_were_set
|
245
|
+
@set
|
246
|
+
end
|
247
|
+
|
248
|
+
def rails_env
|
249
|
+
@set ||= {}
|
250
|
+
@set[:rails_env]
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
254
|
+
session_klass.set_channel = {:host => server}
|
255
|
+
session_klass.set_roles = roles
|
256
|
+
session_klass.set_environment_settings = cap_variable["environment_settings"]
|
257
|
+
parallel_mocks.each do |mock|
|
258
|
+
mock.call(session_klass)
|
259
|
+
end
|
260
|
+
@parallel_sessions ||= []
|
261
|
+
@parallel_sessions << session_klass
|
262
|
+
session_klass.instance_eval do
|
263
|
+
yield session_klass
|
264
|
+
end
|
265
|
+
end
|
266
|
+
@parallel_sessions
|
267
|
+
end
|