screwcap 0.6.3 → 0.7

Sign up to get free protection for your applications and to get access to all the features.
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.6.3'
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"
@@ -4,74 +4,80 @@ class Runner
4
4
 
5
5
  def self.execute! options
6
6
  @@silent = options[:silent]
7
- begin
8
- _log "\nExecuting task named #{options[:name]} on #{options[:server].name}..\n", :color => :blue
9
- options[:server].__with_connection_for(options[:address]) do |ssh|
10
- options[:commands].each do |command|
11
- ret = run_command(command, ssh, options)
12
- break if ret != 0 and command[:abort_on_fail] == true
13
- end
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
- def self.execute_locally! options
24
- @@silent = options[:silent]
25
- _log "\n*** BEGIN executing local task #{options[:name]}\n", :color => :blue
26
- options[:commands].each do |command|
27
- command[:stdout] = ret = `#{command[:command]}`
28
-
29
- if $?.to_i == 0
30
- _log " I: (local): #{command[:command]}\n", :color => :blue
31
- _log(" O: (local): #{ret}\n", :color => :blue) unless ret.nil? or ret == ""
32
- else
33
- _log " I: (local): #{command[:command]}\n", :color => :blue
34
- _errorlog(" O: (local): #{ret}\n", :color => :red) unless ret.nil? or ret == ""
35
- _errorlog(" E: (local): #{command[:command]} return exit code: #{$?}\n", :color => :red) if $? != 0
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*** END executing local task #{options[:name]}\n", :color => :blue
39
- options[:commands]
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.run_command(command, ssh, options)
45
- case command[:type]
46
- when :remote
47
- stdout, stderr, exit_code, exit_signal = ssh_exec! ssh, command[:command]
48
- command[:stdout] = stdout
49
- command[:stderr] = stderr
50
- command[:exit_code] = exit_code
51
- if exit_code == 0
52
- _log(".", :color => :green)
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
- return $?
66
- when :scp
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
@@ -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 __with_connection_for(address, &block)
37
- if self.__gateway
38
- __gateway.__get_gateway_connection.ssh(address, self.__user, options_for_net_ssh) do |ssh|
39
- yield ssh
40
- end
41
- else
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 __upload_to!(address, local, remote)
49
- self.__with_connection_for(address) {|ssh| ssh.scp.upload! local, remote }
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) unless options[:local] == true
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
- commands = task.__build_commands(self.__command_sets)
191
- if task.__options[:local] == true
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
@@ -16,7 +16,7 @@ require 'screwcap/sequence'
16
16
  require 'screwcap/task_manager'
17
17
 
18
18
  module Screwcap
19
- VERSION='0.6.3'
19
+ VERSION='0.7'
20
20
 
21
21
  class TaskNotFound < RuntimeError; end
22
22
  class NoServersDefined < Exception; end
data/screwcap.gemspec CHANGED
@@ -6,7 +6,7 @@ require 'bundler/version'
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "screwcap"
9
- s.version = "0.6.3"
9
+ s.version = "0.7"
10
10
  s.platform = Gem::Platform::RUBY
11
11
  s.author = "Grant Ammons"
12
12
  s.email = ["grant@pipelinedealsco.com"]
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
- :server => @server,
18
- :address => "fake.com",
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
- :server => @server,
33
- :address => "fake.com",
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 == "ok\n"
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, :local => true do
46
- run "echo 'bongle'"
46
+ task = Task.new :name => :localtest do
47
+ local "echo 'bongle'"
47
48
  end
48
- commands = Runner.execute_locally! :name => :localtest, :commands => task.__build_commands, :silent => true
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
- :server => @server,
60
- :address => "fake.com",
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
@@ -27,6 +27,6 @@ describe "Servers" do
27
27
 
28
28
  it "should provide a connection to the server" do
29
29
  server = Server.new(:name => :test, :user => :root, :address => "abc.com")
30
- server.should respond_to(:__with_connection_for)
30
+ server.should respond_to(:connect!)
31
31
  end
32
32
  end
@@ -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).__command_sets
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(1).__command_sets
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(1).__command_sets
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(1).__command_sets
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: 1
4
+ hash: 5
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 6
9
- - 3
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-18 00:00:00 -04:00
17
+ date: 2011-03-29 00:00:00 -04:00
19
18
  default_executable:
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency