chef_cap 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|