screwcap 0.3.2 → 0.3.3
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/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:
|