screwcap 0.3.2 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +0 -1
- data/README.rdoc +5 -9
- data/Rakefile +1 -1
- data/lib/screwcap/base.rb +0 -7
- data/lib/screwcap/deployer.rb +133 -3
- data/lib/screwcap/runner.rb +2 -2
- data/lib/screwcap/sequence.rb +20 -0
- data/lib/screwcap/server.rb +13 -1
- data/lib/screwcap/task.rb +47 -4
- data/lib/screwcap.rb +1 -2
- data/screwcap.gemspec +1 -1
- data/spec/task_spec.rb +1 -1
- metadata +5 -6
- data/lib/exts.rb +0 -5
data/Manifest.txt
CHANGED
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
=
|
1
|
+
= Screwcap
|
2
2
|
|
3
|
-
* http://github.com
|
3
|
+
* http://gammons.github.com/screwcap
|
4
4
|
|
5
5
|
== DESCRIPTION:
|
6
6
|
|
@@ -8,18 +8,14 @@ Screwcap is a library that wraps Net::SSH and makes it easy to perform actions o
|
|
8
8
|
|
9
9
|
The most obvious task you could use screwcap for is deploying rails applications to remote servers.
|
10
10
|
|
11
|
-
== FEATURES
|
11
|
+
== FEATURES:
|
12
12
|
|
13
|
-
*
|
13
|
+
*
|
14
14
|
|
15
15
|
== SYNOPSIS:
|
16
16
|
|
17
17
|
FIX (code sample of usage)
|
18
18
|
|
19
|
-
== REQUIREMENTS:
|
20
|
-
|
21
|
-
* FIX (list of requirements)
|
22
|
-
|
23
19
|
== INSTALL:
|
24
20
|
|
25
21
|
* gem install screwcap
|
@@ -28,7 +24,7 @@ The most obvious task you could use screwcap for is deploying rails applications
|
|
28
24
|
|
29
25
|
(The MIT License)
|
30
26
|
|
31
|
-
Copyright (c) 2010 Grant Ammons
|
27
|
+
Copyright (c) 2010 Grant Ammons (grant@pipelinedeals.com)
|
32
28
|
|
33
29
|
Permission is hereby granted, free of charge, to any person obtaining
|
34
30
|
a copy of this software and associated documentation files (the
|
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.3.
|
14
|
+
self.version = '0.3.3'
|
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/lib/screwcap/base.rb
CHANGED
@@ -1,12 +1,5 @@
|
|
1
1
|
module Screwcap
|
2
2
|
class Base < OpenStruct
|
3
|
-
|
4
|
-
# for debugging purposes only
|
5
|
-
#def method_missing(m, *args, &block)
|
6
|
-
# $stdout << "For #{self.class.to_s}: calling #{m}\n"
|
7
|
-
# super(m, args.first)
|
8
|
-
#end
|
9
|
-
|
10
3
|
def set(var, *args)
|
11
4
|
method_missing((var.to_s + "=").to_sym, args.first)
|
12
5
|
end
|
data/lib/screwcap/deployer.rb
CHANGED
@@ -27,6 +27,67 @@ class Deployer < Screwcap::Base
|
|
27
27
|
|
28
28
|
|
29
29
|
# create a task. Minimally, a task needs a :server specified to run the task on.
|
30
|
+
# task_for :herd_the_cats, :server => :cat_server do
|
31
|
+
# ...
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# task_for :herd_both, :servers => [:cat_server, :dog_server] do
|
35
|
+
# ...
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# The only other option that task_for accepts is *:parallel*. If *parallel* => false, and a server has multiple addresses, then the task will be run serially, rather than all at the same time.
|
39
|
+
#
|
40
|
+
# server :pig, :address => "pig.com"
|
41
|
+
# server :horse, :address => "horse.com"
|
42
|
+
# server :cats_and_dogs, :addresses => ["cat.com","dog.com"]
|
43
|
+
#
|
44
|
+
# task_for :one_at_a_time, :servers => [:pig, :horse], :parallel => false
|
45
|
+
# run "serial_task" # this will be run on the pig server first, then on the horse server.
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# task_for :one_at_a_time_2, :server => :cats_and_dogs, :parallel => false do
|
49
|
+
# run "serial_task" # this will be run on cat.com first, then on dog.com
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# task_for :all_together_now, :servers => [:pig, :horse, :cats_and_dogs] do
|
53
|
+
# run "parallel_task" # this task will be executed on all the addresses specified by the servers at the same time.
|
54
|
+
# end
|
55
|
+
# ===A note about variable scope
|
56
|
+
# ====Any variables you declare within a task are scoped to that task.
|
57
|
+
#
|
58
|
+
# # in your deployment recipe...
|
59
|
+
# set :animal, "donkey"
|
60
|
+
#
|
61
|
+
# task_for :thing, :server => :server do
|
62
|
+
# set :animal, "pig"
|
63
|
+
# run "pet #{animal}" # will run 'pet pig'
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# ====A command set that is called by a task will inherit all the variables set by the task.
|
67
|
+
#
|
68
|
+
# command_set :pet_the_animal do
|
69
|
+
# run "pet #{animal}"
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# task_for :pet_pig do
|
73
|
+
# set :animal, "pig"
|
74
|
+
# pet_the_animal
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# task_for :pet_horse do
|
78
|
+
# set :animal, "horse"
|
79
|
+
# pet_the_animal
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# ====Of course, you can also set variables within the command set as well, that are scoped only to that command set.
|
83
|
+
#
|
84
|
+
# command_set :pet_the_donkey do
|
85
|
+
# set :animal, "donkey"
|
86
|
+
# run "pet #{donkey}"
|
87
|
+
# end
|
88
|
+
#
|
89
|
+
# ====Any command sets that are nested within another command set will inerit all the variables from the parent command set.
|
90
|
+
#
|
30
91
|
def task_for name, options = {}, &block
|
31
92
|
t = Task.new(options.merge(:name => name, :nocolor => self.__options[:nocolor], :silent => self.__options[:silent], :deployment_servers => self.__servers, :command_sets => self.__command_sets), &block)
|
32
93
|
clone_table_for(t)
|
@@ -34,27 +95,92 @@ class Deployer < Screwcap::Base
|
|
34
95
|
self.__tasks << t
|
35
96
|
end
|
36
97
|
|
98
|
+
# ====A *command set* is like a generic set of tasks that you intend to use in multiple tasks.
|
99
|
+
#
|
100
|
+
# command_set :redundant_task do
|
101
|
+
# run "redundant_task"
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# task_for :pet_pig, :server => :s1 do
|
105
|
+
# redundant_task
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# task_for :pet_horse, :server => s2 do
|
109
|
+
# redundant_task
|
110
|
+
# end
|
111
|
+
#
|
112
|
+
# ====You can also nest command sets within other command sets.
|
113
|
+
# command_set :other_task do
|
114
|
+
# run "other_task"
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# command_set :redundant_task do
|
118
|
+
# other_task
|
119
|
+
# end
|
120
|
+
#
|
121
|
+
# task_for :pet_horse, :server => s2 do
|
122
|
+
# redundant_task
|
123
|
+
# end
|
124
|
+
|
125
|
+
|
37
126
|
def command_set(name,options = {},&block)
|
38
127
|
t = Task.new(options.merge(:name => name, :validate => false, :command_set => true, :command_sets => self.__command_sets), &block)
|
39
128
|
clone_table_for(t)
|
40
129
|
self.__command_sets << t
|
41
130
|
end
|
42
131
|
|
132
|
+
# ====A *server* is the address(es) that you run a *:task* on.
|
133
|
+
# server :myserver, :address => "abc.com", :password => "xxx"
|
134
|
+
# server :app_servers, :addresses => ["abc.com","def.com"], :keys => "~/.ssh/my_key"
|
135
|
+
#
|
136
|
+
# ==== Options
|
137
|
+
# * A server must have a *:user*.
|
138
|
+
# * Specify *:address* or *:addresses*
|
139
|
+
# * A *:gateway*. See the section about gateways for more info.
|
140
|
+
# * All Other options will be passed directly to Net::SSH.
|
141
|
+
# * *:keys* can be used to specify the key to use to connect to the server
|
142
|
+
# * *:password* specify the password to connect with. Not recommended. Use keys.
|
43
143
|
def server(name, options = {}, &block)
|
44
144
|
server = Server.new(options.merge(:name => name, :servers => self.__servers, :silent => self.__options[:silent]))
|
45
145
|
self.__servers << server
|
46
146
|
end
|
47
147
|
|
148
|
+
# ====A *Gateway* is an intermediary computer between you and a *:server*.
|
149
|
+
# gateway :mygateway, :address => "abc.com", :keys => "~/.ssh/key"
|
150
|
+
# server :myserver, :address => "192.168.1.2", :password => "xxx", :gateway => :mygateway
|
151
|
+
#
|
152
|
+
# * Gateways have the same option as a *:server*.
|
153
|
+
# * You can specify :gateway => :mygateway in the *:server* definition.
|
48
154
|
def gateway(name, options = {}, &block)
|
49
155
|
server = Server.new(options.merge(:name => name, :is_gateway => true))
|
50
156
|
self.__servers << server
|
51
157
|
end
|
52
158
|
|
159
|
+
# ====A *Sequence* will run a set of tasks in order.
|
160
|
+
#
|
161
|
+
# task_for :do_this, :server => :myserver
|
162
|
+
# ...
|
163
|
+
# end
|
164
|
+
#
|
165
|
+
# task_for :do_that, :server => :myserver
|
166
|
+
# ...
|
167
|
+
# end
|
168
|
+
#
|
169
|
+
# task_for :do_the_other_thing, :server => :myserver
|
170
|
+
# ...
|
171
|
+
# end
|
172
|
+
#
|
173
|
+
# sequence :do_them_all, :tasks => [:do_this, :do_that, :do_the_other_thing]
|
174
|
+
#
|
175
|
+
# ====Sequences can be called just like tasks.
|
176
|
+
# ====Options
|
177
|
+
# * :tasks - the list of tasks to run, as an array of symbols.
|
53
178
|
def sequence(name, options = {}, &block)
|
54
179
|
self.__sequences << Sequence.new(options.merge(:name => name, :deployment_task_names => self.__tasks.map(&:name)))
|
55
180
|
end
|
56
181
|
|
57
|
-
|
182
|
+
# ====Run one or more tasks or sequences.
|
183
|
+
# * :tasks - the list of tasks to run, as an array of symbols.
|
58
184
|
def run!(*tasks)
|
59
185
|
tasks.flatten!
|
60
186
|
# sanity check each task
|
@@ -75,8 +201,12 @@ class Deployer < Screwcap::Base
|
|
75
201
|
$stdout << "\033[0m"
|
76
202
|
end
|
77
203
|
|
78
|
-
# dynamically include another file into an existing configuration file.
|
79
|
-
#
|
204
|
+
# ====Use will dynamically include another file into an existing configuration file.
|
205
|
+
# Screwcap currently looks in the same directory as the current recipe file.
|
206
|
+
#
|
207
|
+
# if you have my_deployment_tasks.rb in the same directory as your recipe file...
|
208
|
+
# and my deployment_tasks includes more tasks, servers, sequences, etc.
|
209
|
+
# use :my_deployment_tasks
|
80
210
|
def use arg
|
81
211
|
if arg.is_a? Symbol
|
82
212
|
begin
|
data/lib/screwcap/runner.rb
CHANGED
@@ -80,8 +80,8 @@ class Runner
|
|
80
80
|
if command[:type] == :remote
|
81
81
|
log " I: (#{address}): #{command[:command]}\n", :color => :green
|
82
82
|
stdout, stderr, exit_code, exit_signal = ssh_exec! ssh, command[:command]
|
83
|
-
log(" O: (#{address}): #{stdout}", :color => :green) unless stdout.
|
84
|
-
errorlog(" O: (#{address}): #{stderr}", :color => :red) unless stderr.
|
83
|
+
log(" O: (#{address}): #{stdout}", :color => :green) unless stdout.nil?
|
84
|
+
errorlog(" O: (#{address}): #{stderr}", :color => :red) unless stderr.nil?
|
85
85
|
errorlog(" E: (#{address}): #{command[:command]} return exit code: #{exit_code}\n", :color => :red) if exit_code != 0
|
86
86
|
return exit_code
|
87
87
|
elsif command[:type] == :local
|
data/lib/screwcap/sequence.rb
CHANGED
@@ -1,4 +1,24 @@
|
|
1
1
|
class Sequence < Screwcap::Base
|
2
|
+
|
3
|
+
# ====A *Sequence* will run a set of tasks in order.
|
4
|
+
#
|
5
|
+
# task_for :do_this, :server => :myserver
|
6
|
+
# ...
|
7
|
+
# end
|
8
|
+
#
|
9
|
+
# task_for :do_that, :server => :myserver
|
10
|
+
# ...
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# task_for :do_the_other_thing, :server => :myserver
|
14
|
+
# ...
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# sequence :do_them_all, :tasks => [:do_this, :do_that, :do_the_other_thing]
|
18
|
+
#
|
19
|
+
# ====Sequences can be called just like tasks.
|
20
|
+
# ====Options
|
21
|
+
# * :tasks - the list of tasks to run, as an array of symbols.
|
2
22
|
def initialize(opts = {})
|
3
23
|
super
|
4
24
|
self.__options = opts
|
data/lib/screwcap/server.rb
CHANGED
@@ -1,4 +1,16 @@
|
|
1
1
|
class Server < Screwcap::Base
|
2
|
+
|
3
|
+
# ====A *server* is the address(es) that you run a *:task* on.
|
4
|
+
# server :myserver, :address => "abc.com", :password => "xxx"
|
5
|
+
# server :app_servers, :addresses => ["abc.com","def.com"], :keys => "~/.ssh/my_key"
|
6
|
+
#
|
7
|
+
# ==== Options
|
8
|
+
# * A server must have a *:user*.
|
9
|
+
# * Specify *:address* or *:addresses*
|
10
|
+
# * A *:gateway*. See the section about gateways for more info.
|
11
|
+
# * All Other options will be passed directly to Net::SSH.
|
12
|
+
# * *:keys* can be used to specify the key to use to connect to the server
|
13
|
+
# * *:password* specify the password to connect with. Not recommended. Use keys.
|
2
14
|
def initialize(opts = {})
|
3
15
|
super
|
4
16
|
self.__options = opts
|
@@ -44,7 +56,7 @@ class Server < Screwcap::Base
|
|
44
56
|
private
|
45
57
|
|
46
58
|
def validate
|
47
|
-
raise Screwcap::InvalidServer, "Please specify an address for the server #{self.__options[:name]}." if self.__addresses.
|
59
|
+
raise Screwcap::InvalidServer, "Please specify an address for the server #{self.__options[:name]}." if self.__addresses.nil? or self.__addresses.size == 0
|
48
60
|
raise Screwcap::InvalidServer, "Please specify a username to use for the server #{self.__name}." if self.__user.nil?
|
49
61
|
raise Screwcap::InvalidServer, "A gateway can have only one address" if self.__addresses.size > 1 and self.__options[:is_gateway] == true
|
50
62
|
end
|
data/lib/screwcap/task.rb
CHANGED
@@ -20,7 +20,31 @@ class Task < Screwcap::Base
|
|
20
20
|
validate(opts[:deployment_servers]) unless opts[:validate] == false
|
21
21
|
end
|
22
22
|
|
23
|
-
#
|
23
|
+
# Run a command. This can either be a string, or a symbol that is the name of a command set to run.
|
24
|
+
#
|
25
|
+
#
|
26
|
+
# command_set :list_of_tasks do
|
27
|
+
# run "do_this"
|
28
|
+
# run "do_that"
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# task_for :item, :servers => :server do
|
32
|
+
# run "ls -l"
|
33
|
+
# list_of_tasks
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# Run also takes a list of *options*, notably :onfailure. If :onfailure is given, and the command specified by run
|
37
|
+
# returns a non-zero status, screwcap will then abort the task and run the command set specified by :onfailure.
|
38
|
+
#
|
39
|
+
#
|
40
|
+
# command_set :rollback do
|
41
|
+
# run "rollback_this"
|
42
|
+
# run "rollback_that"
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# task_for :item, :servers => :server do
|
46
|
+
# run "ls -l", :onfailure => :rollback
|
47
|
+
# end
|
24
48
|
def run arg, options = {}
|
25
49
|
|
26
50
|
if arg.class == Symbol
|
@@ -30,11 +54,30 @@ class Task < Screwcap::Base
|
|
30
54
|
end
|
31
55
|
end
|
32
56
|
|
57
|
+
# SCP a file from your local drive to a remote machine.
|
58
|
+
# task_for :item, :servers => :server do
|
59
|
+
# scp :local => "/tmp/pirate_booty", :remote => "/mnt/app/config/booty.yml"
|
60
|
+
# end
|
33
61
|
def scp options = {}
|
34
62
|
self.__commands << options.merge({:type => :scp})
|
35
63
|
end
|
36
64
|
|
37
|
-
#
|
65
|
+
# Run a command locally. This can either be a string, or a symbol that is the name of a command set to run.
|
66
|
+
#
|
67
|
+
# task_for :item, :servers => :server do
|
68
|
+
# local "prepare_the_cats"
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# local also takes a hash of *options*, notably :onfailure. If :onfailure is given, and the command specified by run
|
72
|
+
# returns a non-zero status, screwcap will then abort the task and run the command set specified by :onfailure.
|
73
|
+
#
|
74
|
+
# command_set :rollback do
|
75
|
+
# run "rollback_this"
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# task_for :item, :servers => :server do
|
79
|
+
# local "herd_cats", :onfailure => :rollback
|
80
|
+
# end
|
38
81
|
def local arg, options = {}
|
39
82
|
if arg.class == Symbol
|
40
83
|
self.__commands << options.merge({:command => self.send(arg), :type => :local, :from => self.__name})
|
@@ -50,7 +93,7 @@ class Task < Screwcap::Base
|
|
50
93
|
|
51
94
|
protected
|
52
95
|
|
53
|
-
def method_missing(m, *args)
|
96
|
+
def method_missing(m, *args) # :nodoc
|
54
97
|
if m.to_s[0..1] == "__" or [:run].include?(m) or m.to_s.reverse[0..0] == "="
|
55
98
|
super(m, args.first)
|
56
99
|
else
|
@@ -75,7 +118,7 @@ class Task < Screwcap::Base
|
|
75
118
|
end
|
76
119
|
|
77
120
|
def validate(servers)
|
78
|
-
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 self.__server_names.
|
121
|
+
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 self.__server_names.nil? or self.__server_names == []
|
79
122
|
|
80
123
|
self.__server_names.each do |server_name|
|
81
124
|
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 servers.map(&:name).include?(server_name)
|
data/lib/screwcap.rb
CHANGED
@@ -7,7 +7,6 @@ require 'net/ssh/gateway'
|
|
7
7
|
require 'ostruct'
|
8
8
|
require 'logger'
|
9
9
|
|
10
|
-
require 'exts'
|
11
10
|
require 'screwcap/message_logger'
|
12
11
|
require 'screwcap/base'
|
13
12
|
require 'screwcap/task'
|
@@ -17,7 +16,7 @@ require 'screwcap/sequence'
|
|
17
16
|
require 'screwcap/deployer'
|
18
17
|
|
19
18
|
module Screwcap
|
20
|
-
VERSION='0.3.
|
19
|
+
VERSION='0.3.3'
|
21
20
|
|
22
21
|
class TaskNotFound < RuntimeError; end
|
23
22
|
class NoServersDefined < Exception; end
|
data/screwcap.gemspec
CHANGED
data/spec/task_spec.rb
CHANGED
@@ -28,7 +28,7 @@ describe "Tasks" do
|
|
28
28
|
it "should be able to execute statements on a remote server" do
|
29
29
|
task = @deployer.__tasks.find {|t| t.name == :task1 }
|
30
30
|
Runner.execute! task, @deployer.__options
|
31
|
-
@stderr.should ==
|
31
|
+
@stderr.size.should == 12
|
32
32
|
@stdout.size.should == 26
|
33
33
|
end
|
34
34
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: screwcap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 3
|
9
|
-
-
|
10
|
-
version: 0.3.
|
9
|
+
- 3
|
10
|
+
version: 0.3.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Grant Ammons
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-10-
|
18
|
+
date: 2010-10-28 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -120,7 +120,6 @@ files:
|
|
120
120
|
- Rakefile
|
121
121
|
- TODO
|
122
122
|
- bin/screwcap
|
123
|
-
- lib/exts.rb
|
124
123
|
- lib/screwcap.rb
|
125
124
|
- lib/screwcap/base.rb
|
126
125
|
- lib/screwcap/deployer.rb
|
@@ -152,7 +151,7 @@ files:
|
|
152
151
|
- test/config/upload.rb
|
153
152
|
- test/config/use.rb
|
154
153
|
has_rdoc: true
|
155
|
-
homepage: http://github.com
|
154
|
+
homepage: http://gammons.github.com/screwcap
|
156
155
|
licenses: []
|
157
156
|
|
158
157
|
post_install_message:
|