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 CHANGED
@@ -6,7 +6,6 @@ README.rdoc
6
6
  Rakefile
7
7
  TODO
8
8
  bin/screwcap
9
- lib/exts.rb
10
9
  lib/screwcap.rb
11
10
  lib/screwcap/base.rb
12
11
  lib/screwcap/deployer.rb
data/README.rdoc CHANGED
@@ -1,6 +1,6 @@
1
- = screwcap
1
+ = Screwcap
2
2
 
3
- * http://github.com/#{github_username}/#{project_name}
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/PROBLEMS:
11
+ == FEATURES:
12
12
 
13
- * Screwcap is currently not ready for production use as it is under active development.
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.2'
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
@@ -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
- # by default, it looks for the include file in the same path as your tasks file you specified.
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
@@ -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.blank?
84
- errorlog(" O: (#{address}): #{stderr}", :color => :red) unless stderr.blank?
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
@@ -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
@@ -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.blank?
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
- # run a command. basically just pass it a string containing the command you want to run.
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
- # run a command. basically just pass it a string containing the command you want to run.
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.blank?
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.2'
19
+ VERSION='0.3.3'
21
20
 
22
21
  class TaskNotFound < RuntimeError; end
23
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.3.2"
9
+ s.version = "0.3.3"
10
10
  s.platform = Gem::Platform::RUBY
11
11
  s.author = "Grant Ammons"
12
12
  s.email = ["grant@pipelinedealsco.com"]
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: 23
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 3
9
- - 2
10
- version: 0.3.2
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-25 00:00:00 -04:00
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/#{github_username}/#{project_name}
154
+ homepage: http://gammons.github.com/screwcap
156
155
  licenses: []
157
156
 
158
157
  post_install_message:
data/lib/exts.rb DELETED
@@ -1,5 +0,0 @@
1
- class Object
2
- def blank?
3
- self.nil? || self.size == 0
4
- end
5
- end