pocketknife 0.1.0 → 0.2.0
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/.travis.yml +5 -0
- data/CHANGES.md +14 -0
- data/Gemfile +24 -9
- data/README.md +48 -18
- data/Rakefile +44 -4
- data/lib/pocketknife.rb +64 -15
- data/lib/pocketknife/errors.rb +101 -61
- data/lib/pocketknife/node.rb +131 -40
- data/lib/pocketknife/node_manager.rb +12 -11
- data/lib/pocketknife/version.rb +6 -1
- data/lib/shellwords.rb +153 -0
- data/pocketknife.gemspec +40 -33
- data/spec/pocketknife_node_spec.rb +64 -20
- data/spec/pocketknife_spec.rb +5 -1
- data/spec/spec_helper.rb +8 -1
- data/spec/support/silence_stream.rb +11 -0
- metadata +97 -43
- data/Gemfile.lock +0 -52
data/.travis.yml
ADDED
data/CHANGES.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Changes
|
2
|
+
=======
|
3
|
+
|
4
|
+
* 0.2.0
|
5
|
+
* [!] Changed default transfer mechanism to `rsync` from `tar` in past versions.
|
6
|
+
* Added transfer mechanisms, allowing the choice of `rsync` and `tar` for uploading files to nodes. The new `rsync` option deals gracefully with symlinks in your sources and is faster for making small changes. See "Transfer mechanisms" section in the README for details. Suggested by Roland Moriz.
|
7
|
+
* Added data bags support, contributed by Richard Livsey.
|
8
|
+
* Added ability to override the runlist. See "Override runlist" in the README for details.
|
9
|
+
* Added shellwords to properly escape strings in commands.
|
10
|
+
* Added clear success message and timer, suggested by Trip Leonard.
|
11
|
+
* Added clearer error message when `nodes` directory is missing.
|
12
|
+
|
13
|
+
* 0.1.0
|
14
|
+
* First release
|
data/Gemfile
CHANGED
@@ -1,13 +1,28 @@
|
|
1
|
-
source
|
1
|
+
source 'http://rubygems.org'
|
2
2
|
|
3
|
-
gem
|
4
|
-
gem
|
3
|
+
gem 'archive-tar-minitar', '~> 0.5.0'
|
4
|
+
gem 'rye', '~> 0.9.0'
|
5
5
|
|
6
6
|
group :development do
|
7
|
-
gem
|
8
|
-
|
9
|
-
gem
|
10
|
-
gem
|
11
|
-
gem
|
12
|
-
gem
|
7
|
+
gem 'rake'
|
8
|
+
|
9
|
+
gem 'bluecloth', '~> 2.2.0'
|
10
|
+
gem 'rspec', '~> 2.10.0'
|
11
|
+
gem 'yard', '~> 0.8.0'
|
12
|
+
gem 'jeweler', '~> 1.8.0'
|
13
|
+
|
14
|
+
# OPTIONAL LIBRARIES: These libraries upset travis-ci and may cause Ruby or
|
15
|
+
# RVM to hang, so only use them when needed.
|
16
|
+
if ENV['DEBUGGER']
|
17
|
+
platform :mri_18 do
|
18
|
+
gem 'rcov', :require => false
|
19
|
+
gem 'ruby-debug'
|
20
|
+
end
|
21
|
+
|
22
|
+
platform :mri_19 do
|
23
|
+
gem 'simplecov', :require => false
|
24
|
+
gem 'debugger-ruby_core_source'
|
25
|
+
gem 'debugger'
|
26
|
+
end
|
27
|
+
end
|
13
28
|
end
|
data/README.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
[](http://travis-ci.org/igal/pocketknife)
|
2
|
+
|
1
3
|
pocketknife
|
2
4
|
===========
|
3
5
|
|
@@ -38,24 +40,6 @@ Go into your new *project* directory:
|
|
38
40
|
|
39
41
|
Create cookbooks in the `cookbooks` directory that describe how your computers should be configured. These are standard `chef` cookbooks, like the [opscode/cookbooks](https://github.com/opscode/cookbooks). For example, download a copy of [opscode/cookbooks/ntp](https://github.com/opscode/cookbooks/tree/master/ntp) as `cookbooks/ntp`.
|
40
42
|
|
41
|
-
Override cookbooks in the `site-cookbooks` directory. This has the same structure as `cookbooks`, but any files you put here will override the contents of `cookbooks`. This is useful for storing the original code of a third-party cookbook in `cookbooks` and putting your customizations in `site-cookbooks`.
|
42
|
-
|
43
|
-
Optionally define roles in the `roles` directory that describe common behavior and attributes of your computers using JSON syntax using [chef's documentation](http://wiki.opscode.com/display/chef/Roles#Roles-AsJSON). For example, define a role called `ntp_client` by creating a file called `roles/ntp_client.json` with this content:
|
44
|
-
|
45
|
-
{
|
46
|
-
"name": "ntp_client",
|
47
|
-
"chef_type": "role",
|
48
|
-
"json_class": "Chef::Role",
|
49
|
-
"run_list": [
|
50
|
-
"recipe[ntp]"
|
51
|
-
],
|
52
|
-
"override_attributes": {
|
53
|
-
"ntp": {
|
54
|
-
"servers": ["0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org", "3.pool.ntp.org"]
|
55
|
-
}
|
56
|
-
}
|
57
|
-
}
|
58
|
-
|
59
43
|
Define a new node using the `chef` JSON syntax for [runlist](http://wiki.opscode.com/display/chef/Setting+the+run_list+in+JSON+during+run+time) and [attributes](http://wiki.opscode.com/display/chef/Attributes). For example, to define a node with the hostname `henrietta.swa.gov.it` create the `nodes/henrietta.swa.gov.it.json` file, and add the contents below so it uses the `ntp_client` role and overrides its attributes to use a local NTP server:
|
60
44
|
|
61
45
|
{
|
@@ -79,6 +63,52 @@ When deploying a configuration to a node, `pocketknife` will check whether Chef
|
|
79
63
|
|
80
64
|
To always install Chef and its dependencies when they're needed, without prompts, use the `-i` option, e.g. `pocketknife -i henrietta`. Or to never install Chef and its dependencies, use the `-I` option, which will cause the program to quit with an error rather than prompting if Chef or its dependencies aren't installed.
|
81
65
|
|
66
|
+
Override runlist
|
67
|
+
----------------
|
68
|
+
|
69
|
+
Specify the runlist by using the `-r` option, which will override the one specified in the node, e.g.:
|
70
|
+
|
71
|
+
pocketknife -r mycookbook henrietta
|
72
|
+
|
73
|
+
Transfer mechanisms
|
74
|
+
-------------------
|
75
|
+
|
76
|
+
Files can be uploaded to nodes using different transfer mechanisms:
|
77
|
+
|
78
|
+
* `tar` - Uses one connection for execution of commands and uploads, and sends a tarball that's then extracted. Pros: Pure Ruby, reuses connection. Cons: Can't cope with symlinks, inefficient for sending a small change.
|
79
|
+
* `rsync` - Uses one connection for execution of commands and then runs the `rsync` command to upload files. Pros: Handles symlinks, and is efficient for sending a small change. Cons: Requires `rsync` command, rather than being pure Ruby, and doesn't reuse the connection.
|
80
|
+
|
81
|
+
You can specify the transfer mechanism with the `-t` option and the name of the mechanism, e.g.:
|
82
|
+
|
83
|
+
pocketknife -t rsync henrietta
|
84
|
+
|
85
|
+
Override cookbooks
|
86
|
+
------------------
|
87
|
+
|
88
|
+
Override cookbooks in the `site-cookbooks` directory. This has the same structure as `cookbooks`, but any files you put here will override the contents of `cookbooks`. This is useful for storing the original code of a third-party cookbook in `cookbooks` and putting your customizations in `site-cookbooks`.
|
89
|
+
|
90
|
+
Roles
|
91
|
+
-----
|
92
|
+
|
93
|
+
Optionally define roles in the `roles` directory that describe common behavior and attributes of your computers using JSON syntax using [chef's documentation](http://wiki.opscode.com/display/chef/Roles#Roles-AsJSON). For example, define a role called `ntp_client` by creating a file called `roles/ntp_client.json` with this content:
|
94
|
+
|
95
|
+
{
|
96
|
+
"name": "ntp_client",
|
97
|
+
"chef_type": "role",
|
98
|
+
"json_class": "Chef::Role",
|
99
|
+
"run_list": [
|
100
|
+
"recipe[ntp]"
|
101
|
+
],
|
102
|
+
"override_attributes": {
|
103
|
+
"ntp": {
|
104
|
+
"servers": ["0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org", "3.pool.ntp.org"]
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
Debugging
|
110
|
+
---------
|
111
|
+
|
82
112
|
If something goes wrong while deploying the configuration, you can display verbose logging from `pocketknife` and Chef by using the `-v` option. For example, deploy the configuration to `henrietta` with verbose logging:
|
83
113
|
|
84
114
|
pocketknife -v henrietta
|
data/Rakefile
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
task :default => :spec
|
4
|
+
|
3
5
|
require 'rubygems'
|
4
6
|
require 'bundler'
|
5
7
|
begin
|
@@ -39,6 +41,7 @@ With pocketknife, all of your cookbooks, roles and nodes are stored in easy-to-u
|
|
39
41
|
Gemfile
|
40
42
|
LICENSE.txt
|
41
43
|
README.md
|
44
|
+
CHANGES.md
|
42
45
|
Rakefile
|
43
46
|
lib/*
|
44
47
|
spec/*
|
@@ -47,18 +50,55 @@ With pocketknife, all of your cookbooks, roles and nodes are stored in easy-to-u
|
|
47
50
|
end
|
48
51
|
Jeweler::RubygemsDotOrgTasks.new
|
49
52
|
|
53
|
+
desc "Run coverage report using simplecov."
|
54
|
+
task :simplecov do
|
55
|
+
ENV['SIMPLECOV'] = 'true'
|
56
|
+
Rake::Task['spec'].invoke
|
57
|
+
end
|
58
|
+
|
50
59
|
require 'rspec/core'
|
51
60
|
require 'rspec/core/rake_task'
|
52
61
|
RSpec::Core::RakeTask.new(:spec) do |spec|
|
53
62
|
spec.pattern = FileList['spec/**/*_spec.rb']
|
54
63
|
end
|
55
64
|
|
56
|
-
|
57
|
-
|
58
|
-
spec.
|
65
|
+
def rcov(options=[])
|
66
|
+
# None of the official ways to invoke Rcov work right now. Sigh.
|
67
|
+
cmd = "rcov --exclude osx\/objc,gems\/,spec\/,features\/,lib/shellwords.rb,lib/pocketknife/version.rb #{[options].flatten} $(which rspec) spec/*_spec.rb 2>&1"
|
68
|
+
puts cmd
|
69
|
+
output = `#{cmd}`
|
70
|
+
puts output
|
71
|
+
return output
|
59
72
|
end
|
60
73
|
|
61
|
-
|
74
|
+
RCOV_DATA = 'coverage/rcov.data'
|
75
|
+
RCOV_LOG = 'coverage/rcov.txt'
|
76
|
+
|
77
|
+
namespace :rcov do
|
78
|
+
desc "Save rcov information for use with rcov:diff"
|
79
|
+
task :save do
|
80
|
+
rcov "--save=#{RCOV_DATA}"
|
81
|
+
end
|
82
|
+
|
83
|
+
desc "Generate report of what code changed since last rcov:save"
|
84
|
+
task :diff do
|
85
|
+
output = rcov "--no-color --text-coverage-diff=#{RCOV_DATA}"
|
86
|
+
File.open(RCOV_LOG, 'w+') do |h|
|
87
|
+
h.write output
|
88
|
+
end
|
89
|
+
puts "\nSaved coverage report to: #{RCOV_LOG}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
desc "Run all specs with rcov"
|
94
|
+
task :rcov do
|
95
|
+
rcov
|
96
|
+
end
|
62
97
|
|
63
98
|
require 'yard'
|
64
99
|
YARD::Rake::YardocTask.new
|
100
|
+
|
101
|
+
desc "List undocumented code"
|
102
|
+
task :undoc do
|
103
|
+
system "yardoc --list-undoc | grep -v 'Unrecognized/invalid option'"
|
104
|
+
end
|
data/lib/pocketknife.rb
CHANGED
@@ -2,6 +2,20 @@
|
|
2
2
|
require "pathname"
|
3
3
|
require "fileutils"
|
4
4
|
|
5
|
+
begin
|
6
|
+
require "shellwords"
|
7
|
+
rescue LoadError
|
8
|
+
require "#{File.dirname(__FILE__)}/shellwords"
|
9
|
+
end
|
10
|
+
|
11
|
+
# @!visibility private
|
12
|
+
class Pathname
|
13
|
+
# @!visibility private
|
14
|
+
def shellescape
|
15
|
+
self.to_s.shellescape
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
5
19
|
# Gem libraries
|
6
20
|
require "archive/tar/minitar"
|
7
21
|
require "rye"
|
@@ -43,8 +57,11 @@ class Pocketknife
|
|
43
57
|
# Pocketknife.cli('-h')
|
44
58
|
#
|
45
59
|
# @param [Array<String>] args A list of arguments from the command-line, which may include options (e.g. <tt>-h</tt>).
|
60
|
+
# @return [void]
|
61
|
+
# @raise [SystemExit] Something catastrophic happened, e.g. user passed invalid options to interpreter.
|
46
62
|
def self.cli(args)
|
47
63
|
pocketknife = Pocketknife.new
|
64
|
+
timer = Time.now
|
48
65
|
|
49
66
|
OptionParser.new do |parser|
|
50
67
|
parser.banner = <<-HERE
|
@@ -96,9 +113,18 @@ OPTIONS:
|
|
96
113
|
pocketknife.can_install = false
|
97
114
|
end
|
98
115
|
|
116
|
+
parser.on("-r", "--runlist RUNLIST", "Override runlist with a comma-separated list of recipes and roles") do |v|
|
117
|
+
pocketknife.runlist = v
|
118
|
+
end
|
119
|
+
|
120
|
+
transfer_mechanisms = %w[rsync tar]
|
121
|
+
parser.on("-t", "--transfer MECHANISM", transfer_mechanisms, "Specify transfer mechanism (#{transfer_mechanisms.join(', ')})") do |v|
|
122
|
+
pocketknife.transfer_mechanism = v.to_sym
|
123
|
+
end
|
124
|
+
|
99
125
|
begin
|
100
126
|
arguments = parser.parse!
|
101
|
-
rescue OptionParser::
|
127
|
+
rescue OptionParser::ParseError => e
|
102
128
|
puts parser
|
103
129
|
puts
|
104
130
|
puts "ERROR: #{e}"
|
@@ -126,44 +152,62 @@ OPTIONS:
|
|
126
152
|
if not options[:upload] and not options[:apply]
|
127
153
|
pocketknife.deploy(nodes)
|
128
154
|
end
|
155
|
+
|
156
|
+
pocketknife.say("* SUCCESS! Took #{"%0.2f" % [Time.now-timer]} seconds")
|
129
157
|
rescue NodeError => e
|
130
158
|
puts "! #{e.node}: #{e}"
|
131
159
|
exit -1
|
160
|
+
rescue Errno::ENOENT => e
|
161
|
+
puts "! #{e.message}"
|
162
|
+
exit -1
|
132
163
|
end
|
133
164
|
end
|
134
165
|
end
|
135
166
|
|
136
167
|
# Returns the software's version.
|
137
168
|
#
|
138
|
-
# @return [String] A version string.
|
169
|
+
# @return [String] A version string, e.g. <tt>0.0.1</tt>.
|
139
170
|
def self.version
|
140
|
-
return
|
171
|
+
return Pocketknife::Version::STRING
|
141
172
|
end
|
142
173
|
|
143
|
-
# Amount of detail to display
|
174
|
+
# Amount of detail to display.
|
175
|
+
#
|
176
|
+
# @return [Nil, Boolean] +true+ means verbose, +nil+ means normal, +false+ means quiet.
|
144
177
|
attr_accessor :verbosity
|
145
178
|
|
146
|
-
#
|
179
|
+
# Should Chef and its dependencies be installed automatically if not found on a node?
|
180
|
+
#
|
181
|
+
# @return [Nil, Boolean] +true+ means perform the installation without prompting, +false+ means quit if Chef isn't found, and +nil+ means prompt the user to decide this interactively.
|
147
182
|
attr_accessor :can_install
|
148
183
|
|
149
|
-
#
|
184
|
+
# @return [Pocketknife::NodeManager] This instance's node manager.
|
150
185
|
attr_accessor :node_manager
|
151
186
|
|
152
|
-
#
|
187
|
+
# @return [Nil, String] Override runlist with a comma-separated list of recipes and roles.
|
188
|
+
attr_accessor :runlist
|
189
|
+
|
190
|
+
# @return [Symbol] Use :rsync or :tar to transfer files.
|
191
|
+
attr_accessor :transfer_mechanism
|
192
|
+
|
193
|
+
# Instantiates a new Pocketknife.
|
153
194
|
#
|
154
|
-
# @option [Boolean] verbosity Amount of detail to display. +true+ means verbose, +nil+ means normal, +false+ means quiet.
|
155
|
-
# @option [Boolean] install Install Chef and its dependencies if needed? +true+ means do so automatically, +false+ means don't, and +nil+ means display a prompt to ask the user what to do.
|
195
|
+
# @option [Nil, Boolean] verbosity Amount of detail to display. +true+ means verbose, +nil+ means normal, +false+ means quiet.
|
196
|
+
# @option [Nil, Boolean] install Install Chef and its dependencies if needed? +true+ means do so automatically, +false+ means don't, and +nil+ means display a prompt to ask the user what to do.
|
156
197
|
def initialize(opts={})
|
157
198
|
self.verbosity = opts[:verbosity]
|
158
199
|
self.can_install = opts[:install]
|
200
|
+
self.runlist = opts[:runlist]
|
201
|
+
self.transfer_mechanism = opts[:transfer_mechanism] || :rsync
|
159
202
|
|
160
203
|
self.node_manager = NodeManager.new(self)
|
161
204
|
end
|
162
205
|
|
163
|
-
#
|
206
|
+
# Displays a message, but only if it's important enough.
|
164
207
|
#
|
165
208
|
# @param [String] message The message to display.
|
166
|
-
# @param [Boolean] importance How important is this? +true+ means important, +nil+ means normal, +false+ means unimportant.
|
209
|
+
# @param [Nil, Boolean] importance How important is this? +true+ means important, +nil+ means normal, +false+ means unimportant.
|
210
|
+
# @return [void]
|
167
211
|
def say(message, importance=nil)
|
168
212
|
display = \
|
169
213
|
case self.verbosity
|
@@ -183,8 +227,9 @@ OPTIONS:
|
|
183
227
|
# Creates a new project directory.
|
184
228
|
#
|
185
229
|
# @param [String] project The name of the project directory to create.
|
186
|
-
# @yield
|
230
|
+
# @yield status information to the optionally supplied block.
|
187
231
|
# @yieldparam [String] path The path of the file or directory created.
|
232
|
+
# @return [void]
|
188
233
|
def create(project)
|
189
234
|
self.say("* Creating project in directory: #{project}")
|
190
235
|
|
@@ -206,16 +251,18 @@ OPTIONS:
|
|
206
251
|
return true
|
207
252
|
end
|
208
253
|
|
209
|
-
# Returns a
|
254
|
+
# Returns a node.
|
210
255
|
#
|
211
|
-
# @param[String] name The name of the node.
|
256
|
+
# @param [String] name The name of the node.
|
257
|
+
# @return [Pocketknife::Node]
|
212
258
|
def node(name)
|
213
259
|
return node_manager.find(name)
|
214
260
|
end
|
215
261
|
|
216
262
|
# Deploys configuration to the nodes, calls {#upload} and {#apply}.
|
217
263
|
#
|
218
|
-
# @
|
264
|
+
# @param [Array<String>] nodes A list of node names.
|
265
|
+
# @return [void]
|
219
266
|
def deploy(nodes)
|
220
267
|
node_manager.assert_known(nodes)
|
221
268
|
|
@@ -229,6 +276,7 @@ OPTIONS:
|
|
229
276
|
# Uploads configuration information to remote nodes.
|
230
277
|
#
|
231
278
|
# @param [Array<String>] nodes A list of node names.
|
279
|
+
# @return [void]
|
232
280
|
def upload(nodes)
|
233
281
|
node_manager.assert_known(nodes)
|
234
282
|
|
@@ -242,6 +290,7 @@ OPTIONS:
|
|
242
290
|
# Applies configurations to remote nodes.
|
243
291
|
#
|
244
292
|
# @param [Array<String>] nodes A list of node names.
|
293
|
+
# @return [void]
|
245
294
|
def apply(nodes)
|
246
295
|
node_manager.assert_known(nodes)
|
247
296
|
|
data/lib/pocketknife/errors.rb
CHANGED
@@ -1,85 +1,125 @@
|
|
1
1
|
class Pocketknife
|
2
|
-
# ==
|
2
|
+
# == Error
|
3
3
|
#
|
4
|
-
#
|
5
|
-
class
|
6
|
-
#
|
7
|
-
attr_accessor :node
|
8
|
-
|
9
|
-
# Instantiate a new exception.
|
4
|
+
# Superclass of all Pocketknife errors.
|
5
|
+
class Error < StandardError
|
6
|
+
# == InvalidTransferMechanism
|
10
7
|
#
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
8
|
+
# Exception raised when given an invalid transfer mechanism, e.g. not :tar or :rsync.
|
9
|
+
class InvalidTransferMechanism < Error
|
10
|
+
# @return [Symbol] Transfer mechanism that failed.
|
11
|
+
attr_accessor :mechanism
|
12
|
+
|
13
|
+
def initialize(mechanism)
|
14
|
+
super("Invalid transfer mechanism: #{mechanism}")
|
15
|
+
end
|
16
16
|
end
|
17
|
-
end
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
# == NodeError
|
19
|
+
#
|
20
|
+
# An error with a {Pocketknife::Node}. This is meant to be subclassed by a more specific error.
|
21
|
+
class NodeError < Error
|
22
|
+
# @return [String] The name of the node.
|
23
|
+
attr_accessor :node
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
# Instantiate a new exception.
|
26
|
+
#
|
27
|
+
# @param [String] message The message to display.
|
28
|
+
# @param [String] node The name of the unknown node.
|
29
|
+
def initialize(message, node)
|
30
|
+
self.node = node
|
31
|
+
super(message)
|
32
|
+
end
|
30
33
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
34
|
+
# == NoSuchNode
|
35
|
+
#
|
36
|
+
# Exception raised when asked to perform an operation on an unknown node.
|
37
|
+
class NoSuchNode < NodeError
|
38
|
+
end
|
36
39
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
attr_accessor :command
|
40
|
+
# == UnsupportedInstallationPlatform
|
41
|
+
#
|
42
|
+
# Exception raised when asked to install Chef on a node with an unsupported platform.
|
43
|
+
class UnsupportedInstallationPlatform < NodeError
|
44
|
+
end
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
+
# == NotInstalling
|
47
|
+
#
|
48
|
+
# Exception raised when Chef is not available ohn a node, but user asked not to install it.
|
49
|
+
class NotInstalling < NodeError
|
50
|
+
end
|
46
51
|
|
47
|
-
|
48
|
-
|
52
|
+
# == RsyncError
|
53
|
+
#
|
54
|
+
# Exception raised if rsync command failed.
|
55
|
+
class RsyncError < NodeError
|
56
|
+
# @return [String] Command that failed.
|
57
|
+
attr_accessor :command
|
49
58
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
59
|
+
def initialize(command, node)
|
60
|
+
super("Failed while rsyncing: #{command}", node)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# == ExecutionError
|
65
|
+
#
|
66
|
+
# Exception raised when something goes wrong executing commands against remote host.
|
67
|
+
class ExecutionError < NodeError
|
68
|
+
# @return [String] Command that failed.
|
69
|
+
attr_accessor :command
|
70
|
+
|
71
|
+
# @return [Rye::Err] Cause of exception, a Rye:Err.
|
72
|
+
attr_accessor :cause
|
73
|
+
|
74
|
+
# @return [Boolean] Was execution's output shown immediately? If so, don't include output in message.
|
75
|
+
attr_accessor :immediate
|
76
|
+
|
77
|
+
# Instantiates a new exception.
|
78
|
+
#
|
79
|
+
# @param [String] node The name of the unknown node.
|
80
|
+
# @param [String] command The command that failed.
|
81
|
+
# @param [Rye::Err] cause The actual exception thrown.
|
82
|
+
# @param [Boolean] immediate Was execution's output shown immediately? If so, don't include output in message.
|
83
|
+
def initialize(node, command, cause, immediate)
|
84
|
+
self.command = command
|
85
|
+
self.cause = cause
|
86
|
+
self.immediate = immediate
|
87
|
+
|
88
|
+
message = <<-HERE.chomp
|
62
89
|
Failed while executing commands on node '#{node}'
|
63
90
|
- COMMAND: #{command}
|
64
91
|
- EXIT STATUS: #{cause.exit_status}
|
65
|
-
|
92
|
+
HERE
|
66
93
|
|
67
|
-
|
68
|
-
|
94
|
+
unless immediate
|
95
|
+
message << <<-HERE.chomp
|
69
96
|
|
70
97
|
- STDOUT: #{cause.stdout.to_s.strip}
|
71
98
|
- STDERR: #{cause.stderr.to_s.strip}
|
72
|
-
|
99
|
+
HERE
|
100
|
+
end
|
101
|
+
|
102
|
+
super(message, node)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns exit status.
|
106
|
+
#
|
107
|
+
# @return [Integer] Exit status from execution.
|
108
|
+
def exit_status
|
109
|
+
return self.cause.exit_status
|
110
|
+
end
|
111
|
+
|
73
112
|
end
|
74
113
|
|
75
|
-
super(message, node)
|
76
114
|
end
|
77
115
|
|
78
|
-
# Returns exit status.
|
79
|
-
#
|
80
|
-
# @return [Integer] Exit status from execution.
|
81
|
-
def exit_status
|
82
|
-
return self.cause.exit_status
|
83
|
-
end
|
84
116
|
end
|
117
|
+
|
118
|
+
InvalidTransferMechanism = Pocketknife::Error::InvalidTransferMechanism
|
119
|
+
NodeError = Pocketknife::Error::NodeError
|
120
|
+
NoSuchNode = Pocketknife::Error::NodeError::NoSuchNode
|
121
|
+
UnsupportedInstallationPlatform = Pocketknife::Error::NodeError::UnsupportedInstallationPlatform
|
122
|
+
NotInstalling = Pocketknife::Error::NodeError::NotInstalling
|
123
|
+
RsyncError = Pocketknife::Error::NodeError::RsyncError
|
124
|
+
ExecutionError = Pocketknife::Error::NodeError::ExecutionError
|
85
125
|
end
|