screwcap 0.6.3 → 0.7
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/Rakefile +1 -1
- data/bin/screwcap +1 -0
- data/lib/screwcap/runner.rb +61 -55
- data/lib/screwcap/server.rb +12 -10
- data/lib/screwcap/task.rb +3 -9
- data/lib/screwcap/task_manager.rb +5 -36
- data/lib/screwcap.rb +1 -1
- data/screwcap.gemspec +1 -1
- data/spec/runner_spec.rb +17 -15
- data/spec/server_spec.rb +1 -1
- data/spec/task_manager_spec.rb +4 -13
- data/spec/task_spec.rb +20 -16
- metadata +4 -5
data/Rakefile
CHANGED
@@ -11,7 +11,7 @@ Hoe.plugin :newgem
|
|
11
11
|
# Generate all the Rake tasks
|
12
12
|
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
13
13
|
$hoe = Hoe.spec 'screwcap' do
|
14
|
-
self.version = '0.
|
14
|
+
self.version = '0.7'
|
15
15
|
self.developer 'Grant Ammons', 'grant@pipelinedeals.com'
|
16
16
|
self.rubyforge_name = self.name # TODO this is default value
|
17
17
|
self.extra_deps = [['net-ssh','>= 2.0.23'],['net-ssh-gateway','>=1.0.1'], ['net-scp','>=1.0.4']]
|
data/bin/screwcap
CHANGED
@@ -6,6 +6,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../lib/trollop')
|
|
6
6
|
|
7
7
|
p = Trollop::Parser.new do
|
8
8
|
opt :silent, "Be silent", :short => "s"
|
9
|
+
opt :verbose, "Be verbose", :short => "v"
|
9
10
|
opt :nocolor, "Do not color output"
|
10
11
|
opt :debug, "Turn on debugger. Will print full stacktrace if an exeception was raised", :short => "d"
|
11
12
|
opt :help, "Show this message", :short => "h"
|
data/lib/screwcap/runner.rb
CHANGED
@@ -4,74 +4,80 @@ class Runner
|
|
4
4
|
|
5
5
|
def self.execute! options
|
6
6
|
@@silent = options[:silent]
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
@@verbose = options[:verbose]
|
8
|
+
task = options[:task]
|
9
|
+
|
10
|
+
if (task.__servers.nil? or task.__servers == [] or task.__servers.compact == []) and task.__built_commands.any? {|c| c[:type] == :remote or c[:type] == :scp }
|
11
|
+
raise Screwcap::ConfigurationError, "The task #{task.name} includes remote commands, however no servers were defined for this task."
|
12
|
+
end
|
13
|
+
|
14
|
+
if options[:servers] and task.__servers
|
15
|
+
begin
|
16
|
+
servers = options[:servers].select {|s| task.__servers.include? s.__name }
|
17
|
+
connections = servers.map {|server| server.connect! }.flatten
|
18
|
+
rescue Net::SSH::AuthenticationFailed => e
|
19
|
+
raise Net::SSH::AuthenticationFailed, "Authentication failed for server named #{server.name}. Please check your authentication credentials."
|
14
20
|
end
|
15
|
-
rescue Net::SSH::AuthenticationFailed => e
|
16
|
-
raise Net::SSH::AuthenticationFailed, "Authentication failed for server named #{server.name}. Please check your authentication credentials."
|
17
|
-
ensure
|
18
|
-
_log "\nComplete\n", :color => :blue
|
19
21
|
end
|
20
|
-
options[:commands] # for tests
|
21
|
-
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
23
|
+
_log "\nExecuting task #{task.name}\n", :color => :blue
|
24
|
+
|
25
|
+
task.__built_commands.each do |command|
|
26
|
+
ret = case command[:type]
|
27
|
+
when :remote
|
28
|
+
threads = []
|
29
|
+
connections.each do |connection|
|
30
|
+
threads << Thread.new(connection) do |conn|
|
31
|
+
run_remote_command(command, conn, options)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
threads.each {|t| t.join }
|
35
|
+
when :local
|
36
|
+
ret = `#{command[:command]}`
|
37
|
+
command[:stdout] = ret
|
38
|
+
if $?.to_i == 0
|
39
|
+
if options[:verbose]
|
40
|
+
_log " O: #{ret}\n", :color => :green
|
41
|
+
else
|
42
|
+
_log(".", :color => :green)
|
43
|
+
end
|
44
|
+
else
|
45
|
+
_errorlog(" E: (local): #{command[:command]} return exit code: #{$?}\n", :color => :red) if $? != 0
|
46
|
+
end
|
47
|
+
ret
|
48
|
+
when :scp
|
49
|
+
threads = []
|
50
|
+
servers.each do |server|
|
51
|
+
threads << Thread.new(server) { |_server| _server._upload! command[:local], command[:remote] }
|
52
|
+
end
|
53
|
+
thread.each {|t| t.join }
|
54
|
+
return 0
|
55
|
+
when :block
|
56
|
+
command[:block].call
|
36
57
|
end
|
37
58
|
end
|
38
|
-
_log "\n
|
39
|
-
|
59
|
+
_log "Complete\n", :color => :blue
|
60
|
+
task.__built_commands # for tests
|
40
61
|
end
|
41
62
|
|
42
63
|
private
|
43
64
|
|
44
|
-
def self.
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
_log("
|
65
|
+
def self.run_remote_command(command, ssh, options)
|
66
|
+
stdout, stderr, exit_code, exit_signal = ssh_exec! ssh, command[:command]
|
67
|
+
command[:stdout] = stdout
|
68
|
+
command[:stderr] = stderr
|
69
|
+
command[:exit_code] = exit_code
|
70
|
+
if exit_code == 0
|
71
|
+
if @@verbose
|
72
|
+
_log(" I: #{command[:command]}\n", :color => :green)
|
73
|
+
_log(" O: #{command[:stdout]}\n", :color => :green)
|
53
74
|
else
|
54
|
-
_errorlog("\n E: (#{options[:address]}): #{command[:command]} return exit code: #{exit_code}\n", :color => :red) if exit_code != 0
|
55
|
-
end
|
56
|
-
return exit_code
|
57
|
-
when :local
|
58
|
-
ret = `#{command[:command]}`
|
59
|
-
command[:stdout] = ret
|
60
|
-
if $?.to_i == 0
|
61
75
|
_log(".", :color => :green)
|
62
|
-
else
|
63
|
-
_errorlog("\n E: (local): #{command[:command]} return exit code: #{$?}\n", :color => :red) if $? != 0
|
64
76
|
end
|
65
|
-
|
66
|
-
|
67
|
-
putc "."
|
68
|
-
options[:server].__upload_to!(options[:address], command[:local], command[:remote])
|
69
|
-
|
70
|
-
# this will need to be improved to allow for :onfailure
|
71
|
-
return 0
|
72
|
-
when :block
|
73
|
-
command[:block].call
|
77
|
+
else
|
78
|
+
_errorlog(" E: (#{options[:address]}): #{command[:command]} return exit code: #{exit_code}\n", :color => :red) if exit_code != 0
|
74
79
|
end
|
80
|
+
exit_code
|
75
81
|
end
|
76
82
|
|
77
83
|
# courtesy of flitzwald on stackoverflow
|
data/lib/screwcap/server.rb
CHANGED
@@ -19,6 +19,7 @@ class Server < Screwcap::Base
|
|
19
19
|
|
20
20
|
servers = opts.delete(:servers)
|
21
21
|
self.__gateway = servers.select {|s| s.__options[:is_gateway] == true }.find {|s| s.__name == opts[:gateway] } if servers
|
22
|
+
self.__connections = []
|
22
23
|
|
23
24
|
self.__options = opts
|
24
25
|
|
@@ -33,20 +34,21 @@ class Server < Screwcap::Base
|
|
33
34
|
self
|
34
35
|
end
|
35
36
|
|
36
|
-
def
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
Net::SSH.start(address, self.__user, options_for_net_ssh) do |ssh|
|
43
|
-
yield ssh
|
37
|
+
def connect!
|
38
|
+
self.__addresses.each do |address|
|
39
|
+
if self.__gateway
|
40
|
+
self.__connections << __gateway.__get_gateway_connection.ssh(address, self.__user, options_for_net_ssh)
|
41
|
+
else
|
42
|
+
self.__connections << Net::SSH.start(address, self.__user, options_for_net_ssh)
|
44
43
|
end
|
45
44
|
end
|
45
|
+
self.__connections
|
46
46
|
end
|
47
47
|
|
48
|
-
def
|
49
|
-
self.
|
48
|
+
def upload! (local, remote)
|
49
|
+
self.__connections.each do |conn|
|
50
|
+
conn.scp.upload! local, remote
|
51
|
+
end
|
50
52
|
end
|
51
53
|
|
52
54
|
protected
|
data/lib/screwcap/task.rb
CHANGED
@@ -11,6 +11,7 @@ class Task < Screwcap::Base
|
|
11
11
|
self.__servers = opts.delete(:servers)
|
12
12
|
self.__callback = opts.delete(:callback)
|
13
13
|
self.__block = block
|
14
|
+
self.__built_commands = []
|
14
15
|
|
15
16
|
if self.__options[:before] and self.__options[:before].class != Array
|
16
17
|
self.__options[:before] = [self.__options[:before]]
|
@@ -211,20 +212,13 @@ class Task < Screwcap::Base
|
|
211
212
|
end
|
212
213
|
end
|
213
214
|
|
214
|
-
commands.flatten
|
215
|
+
commands.flatten!
|
216
|
+
self.__built_commands = commands
|
215
217
|
end
|
216
218
|
|
217
219
|
def validate(servers)
|
218
|
-
raise Screwcap::ConfigurationError, "Could not find a server to run this task on. Please specify :server => :servername or :servers => [:server1, :server2] in the task_for directive." if servers == [] or servers.nil?
|
219
|
-
|
220
|
-
# marshal :server into :servers
|
221
220
|
self.__servers = [self.__options.delete(:server)] if self.__options[:server]
|
222
221
|
self.__servers = [self.__servers] if self.__servers.class != Array
|
223
|
-
|
224
|
-
server_names = servers.map {|s| s.__name }
|
225
|
-
self.__servers.each do |server_name|
|
226
|
-
raise Screwcap::ConfigurationError, "Could not find a server to run this task on. Please specify :server => :servername or :servers => [:server1, :server2] in the task_for directive." unless server_names.include?(server_name)
|
227
|
-
end
|
228
222
|
end
|
229
223
|
|
230
224
|
private
|
@@ -10,7 +10,6 @@ class TaskManager < Screwcap::Base
|
|
10
10
|
self.__options = opts
|
11
11
|
self.__tasks = []
|
12
12
|
self.__servers = []
|
13
|
-
self.__command_sets = []
|
14
13
|
self.__sequences = []
|
15
14
|
|
16
15
|
instance_eval(File.read(self.__options[:recipe_file])) if self.__options[:recipe_file]
|
@@ -82,10 +81,11 @@ class TaskManager < Screwcap::Base
|
|
82
81
|
def task name, options = {}, &block
|
83
82
|
t = Task.new(options.merge(:name => name), &block)
|
84
83
|
t.clone_from(self)
|
85
|
-
t.validate(self.__servers)
|
84
|
+
t.validate(self.__servers)
|
86
85
|
self.__tasks << t
|
87
86
|
end
|
88
87
|
alias :task_for :task
|
88
|
+
alias :command_set :task
|
89
89
|
|
90
90
|
# ====A *command set* is like a generic set of tasks that you intend to use in multiple tasks.
|
91
91
|
#
|
@@ -114,12 +114,6 @@ class TaskManager < Screwcap::Base
|
|
114
114
|
# redundant_task
|
115
115
|
# end
|
116
116
|
|
117
|
-
def command_set(name,options = {},&block)
|
118
|
-
t = Task.new(options.merge(:name => name), &block)
|
119
|
-
t.clone_from(self)
|
120
|
-
self.__command_sets << t
|
121
|
-
end
|
122
|
-
|
123
117
|
# ====A *server* is the address(es) that you run a *:task* on.
|
124
118
|
# server :myserver, :address => "abc.com", :password => "xxx"
|
125
119
|
# server :app_servers, :addresses => ["abc.com","def.com"], :keys => "~/.ssh/my_key"
|
@@ -187,37 +181,12 @@ class TaskManager < Screwcap::Base
|
|
187
181
|
|
188
182
|
ret = []
|
189
183
|
tasks_to_run.each do |task|
|
190
|
-
|
191
|
-
|
192
|
-
Runner.execute_locally! :commands => commands, :silent => self.__options[:silent]
|
193
|
-
else
|
194
|
-
threads = []
|
195
|
-
self.__servers.select {|s| task.__servers.include? s.__name }.each do |server|
|
196
|
-
server.__addresses.each do |address|
|
197
|
-
if task.__options[:parallel] == false
|
198
|
-
Runner.execute!(:name => task.__name,
|
199
|
-
:commands => commands,
|
200
|
-
:address => address,
|
201
|
-
:server => server,
|
202
|
-
:silent => self.__options[:silent])
|
203
|
-
else
|
204
|
-
threads << Thread.new(server,address) do |server, address|
|
205
|
-
Runner.execute!(:name => task.__name,
|
206
|
-
:commands => commands,
|
207
|
-
:address => address,
|
208
|
-
:server => server,
|
209
|
-
:silent => self.__options[:silent])
|
210
|
-
end
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
threads.each {|t| t.join }
|
215
|
-
end
|
216
|
-
ret << commands
|
184
|
+
ret << task.__build_commands(self.__tasks)
|
185
|
+
Runner.execute!(:task => task, :servers => self.__servers, :silent => self.__options[:silent], :verbose => self.__options[:verbose])
|
217
186
|
end
|
218
187
|
|
219
188
|
$stdout << "\033[0m"
|
220
|
-
ret.flatten
|
189
|
+
ret.flatten # return this for tests
|
221
190
|
end
|
222
191
|
|
223
192
|
# ====Use will dynamically include another file into an existing configuration file.
|
data/lib/screwcap.rb
CHANGED
data/screwcap.gemspec
CHANGED
data/spec/runner_spec.rb
CHANGED
@@ -8,15 +8,16 @@ describe "The Runner" do
|
|
8
8
|
run "one"
|
9
9
|
run "two"
|
10
10
|
end
|
11
|
+
@task.__build_commands
|
12
|
+
@task.validate([@server])
|
11
13
|
end
|
12
14
|
|
13
15
|
it "should be able to execute commands on an address of a server" do
|
14
16
|
Runner.stubs(:ssh_exec!).returns(["ok\n","",0,nil])
|
15
17
|
|
16
18
|
commands = Runner.execute! :name => "test",
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:commands => @task.__build_commands,
|
19
|
+
:servers => [@server],
|
20
|
+
:task => @task,
|
20
21
|
:silent => true
|
21
22
|
|
22
23
|
commands[0][:stderr].should == ""
|
@@ -27,25 +28,26 @@ describe "The Runner" do
|
|
27
28
|
end
|
28
29
|
|
29
30
|
it "should be able to handle error commands" do
|
30
|
-
Runner.stubs(:ssh_exec!).returns(["ok\n","",0,nil]).then.returns(["","no\n",1,nil])
|
31
|
+
#Runner.stubs(:ssh_exec!).returns(["ok\n","",0,nil]).then.returns(["","no\n",1,nil])
|
32
|
+
Runner.stubs(:ssh_exec!).returns(["","no\n",1,nil])
|
31
33
|
commands = Runner.execute! :name => "test",
|
32
|
-
:
|
33
|
-
:
|
34
|
-
:commands => @task.__build_commands,
|
34
|
+
:servers => [@server],
|
35
|
+
:task => @task,
|
35
36
|
:silent => true
|
36
37
|
|
37
|
-
commands[0][:stderr].should == ""
|
38
|
-
commands[0][:stdout].should == "
|
38
|
+
commands[0][:stderr].should == "no\n"
|
39
|
+
commands[0][:stdout].should == ""
|
39
40
|
|
40
41
|
commands[1][:stderr].should == "no\n"
|
41
42
|
commands[1][:stdout].should == ""
|
42
43
|
end
|
43
44
|
|
44
45
|
it "should be able to execute local commands" do
|
45
|
-
task = Task.new :name => :localtest
|
46
|
-
|
46
|
+
task = Task.new :name => :localtest do
|
47
|
+
local "echo 'bongle'"
|
47
48
|
end
|
48
|
-
|
49
|
+
task.__build_commands
|
50
|
+
commands = Runner.execute! :name => :localtest, :task => task, :silent => true
|
49
51
|
commands[0][:stdout].should == "bongle\n"
|
50
52
|
end
|
51
53
|
|
@@ -54,11 +56,11 @@ describe "The Runner" do
|
|
54
56
|
task = Task.new :name => :extest do
|
55
57
|
ex { @@testvar = :bango }
|
56
58
|
end
|
59
|
+
task.__build_commands
|
57
60
|
@@testvar.should == :bingo
|
58
61
|
commands = Runner.execute! :name => "test",
|
59
|
-
:
|
60
|
-
:
|
61
|
-
:commands => task.__build_commands,
|
62
|
+
:servers => [@server],
|
63
|
+
:task => task,
|
62
64
|
:silent => true
|
63
65
|
@@testvar.should == :bango
|
64
66
|
end
|
data/spec/server_spec.rb
CHANGED
data/spec/task_manager_spec.rb
CHANGED
@@ -20,12 +20,6 @@ describe "Task Managers" do
|
|
20
20
|
@tm.should have(1).__tasks
|
21
21
|
end
|
22
22
|
|
23
|
-
it "should validate tasks" do
|
24
|
-
@tm = TaskManager.new
|
25
|
-
@tm.server :server, :address => "test", :user => "root"
|
26
|
-
lambda { @tm.task(:deploy, :server => :not_here) { run "test" } }.should raise_error(Screwcap::ConfigurationError)
|
27
|
-
end
|
28
|
-
|
29
23
|
it "should be able to define variables" do
|
30
24
|
@tm.moon_pie= "moon pie"
|
31
25
|
@tm.moon_pie.should == "moon pie"
|
@@ -46,14 +40,13 @@ describe "Task Managers" do
|
|
46
40
|
run "test"
|
47
41
|
end
|
48
42
|
|
49
|
-
@tm.should have(1).
|
43
|
+
@tm.should have(1).__tasks
|
50
44
|
end
|
51
45
|
|
52
46
|
it "should be able to load a recipe file" do
|
53
47
|
@tm = TaskManager.new(:recipe_file => "test/config/super_simple_recipe.rb")
|
54
48
|
@tm.pie.should == "moon pie"
|
55
|
-
@tm.should have(
|
56
|
-
@tm.should have(1).__tasks
|
49
|
+
@tm.should have(2).__tasks
|
57
50
|
end
|
58
51
|
|
59
52
|
it "should be able to execute a recipe" do
|
@@ -70,13 +63,11 @@ describe "Task Managers" do
|
|
70
63
|
it "should be able to include multiple recipe files in a single recipe" do
|
71
64
|
@tm = TaskManager.new(:recipe_file => "test/config/use.rb")
|
72
65
|
@tm.pie.should == "moon pie"
|
73
|
-
@tm.should have(
|
74
|
-
@tm.should have(1).__tasks
|
66
|
+
@tm.should have(2).__tasks
|
75
67
|
|
76
68
|
@tm = TaskManager.new(:recipe_file => "test/config/use2.rb")
|
77
69
|
@tm.pie.should == "moon pie"
|
78
|
-
@tm.should have(
|
79
|
-
@tm.should have(1).__tasks
|
70
|
+
@tm.should have(2).__tasks
|
80
71
|
end
|
81
72
|
|
82
73
|
it "will complain if it can't find the use file" do
|
data/spec/task_spec.rb
CHANGED
@@ -27,6 +27,7 @@ describe "Tasks" do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
commands = task.__build_commands([unknown])
|
30
|
+
task.__built_commands.should_not == []
|
30
31
|
commands.size.should == 2
|
31
32
|
|
32
33
|
commands[0][:type].should == :remote
|
@@ -133,21 +134,6 @@ describe "Tasks" do
|
|
133
134
|
commands.map {|c| c[:command] }.should == ["deploy", "after"]
|
134
135
|
end
|
135
136
|
|
136
|
-
it "should validate" do
|
137
|
-
task = Task.new :name => :test
|
138
|
-
lambda { task.validate([]) }.should raise_error(Screwcap::ConfigurationError)
|
139
|
-
|
140
|
-
server = Server.new :name => :server, :address => "none", :user => "yeah"
|
141
|
-
other_server = Server.new :name => :server2, :address => "none", :user => "yeah"
|
142
|
-
task = Task.new :name => :test, :server => :server
|
143
|
-
lambda { task.validate([server]) }.should_not raise_error
|
144
|
-
lambda { task.validate([other_server]) }.should raise_error(Screwcap::ConfigurationError)
|
145
|
-
task = Task.new :name => :test, :servers => :server
|
146
|
-
lambda { task.validate([server]) }.should_not raise_error
|
147
|
-
task = Task.new :name => :test, :servers => [:server, :server2]
|
148
|
-
lambda { task.validate([server,other_server]) }.should_not raise_error
|
149
|
-
end
|
150
|
-
|
151
137
|
it "should handle before and after inside a task" do
|
152
138
|
do_other_task = Task.new :name => :other_task do
|
153
139
|
run "task"
|
@@ -281,12 +267,30 @@ describe "Tasks" do
|
|
281
267
|
end
|
282
268
|
|
283
269
|
it "has an ex command" do
|
284
|
-
|
285
270
|
task = Task.new :name => :task do
|
286
271
|
@test = "asdf"
|
287
272
|
ex { @test = "fdsa" }
|
288
273
|
end
|
289
274
|
|
290
275
|
commands = task.__build_commands([task])
|
276
|
+
commands[0][:type].should == :block
|
277
|
+
end
|
278
|
+
|
279
|
+
it "should be able to call other tasks" do
|
280
|
+
t1 = Task.new :name => :release_the_hounds do
|
281
|
+
run "release_the_hounds"
|
282
|
+
end
|
283
|
+
|
284
|
+
t2 = Task.new :name => :lock_the_gate do
|
285
|
+
run "lock_the_gate"
|
286
|
+
end
|
287
|
+
|
288
|
+
t3 = Task.new :name => :release_the_hounds_and_lock_the_gate do
|
289
|
+
release_the_hounds
|
290
|
+
lock_the_gate
|
291
|
+
end
|
292
|
+
|
293
|
+
commands = t3.__build_commands [t1, t2]
|
294
|
+
commands.map {|c| c[:command] }.should == %w(release_the_hounds lock_the_gate)
|
291
295
|
end
|
292
296
|
end
|
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: screwcap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 5
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
|
10
|
-
version: 0.6.3
|
8
|
+
- 7
|
9
|
+
version: "0.7"
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Grant Ammons
|
@@ -15,7 +14,7 @@ autorequire:
|
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
16
|
|
18
|
-
date: 2011-03-
|
17
|
+
date: 2011-03-29 00:00:00 -04:00
|
19
18
|
default_executable:
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|