pd-blender 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rubocop.yml +2 -0
- data/.travis.yml +10 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +14 -0
- data/README.md +342 -0
- data/Rakefile +21 -0
- data/bin/blend +20 -0
- data/blender.gemspec +36 -0
- data/lib/blender.rb +67 -0
- data/lib/blender/cli.rb +71 -0
- data/lib/blender/configuration.rb +45 -0
- data/lib/blender/discovery.rb +41 -0
- data/lib/blender/drivers/base.rb +40 -0
- data/lib/blender/drivers/compound.rb +29 -0
- data/lib/blender/drivers/ruby.rb +55 -0
- data/lib/blender/drivers/shellout.rb +63 -0
- data/lib/blender/drivers/ssh.rb +93 -0
- data/lib/blender/drivers/ssh_multi.rb +102 -0
- data/lib/blender/event_dispatcher.rb +45 -0
- data/lib/blender/exceptions.rb +26 -0
- data/lib/blender/handlers/base.rb +39 -0
- data/lib/blender/handlers/doc.rb +73 -0
- data/lib/blender/job.rb +73 -0
- data/lib/blender/lock/flock.rb +64 -0
- data/lib/blender/log.rb +24 -0
- data/lib/blender/rspec.rb +68 -0
- data/lib/blender/rspec/stub_registry.rb +45 -0
- data/lib/blender/scheduled_job.rb +66 -0
- data/lib/blender/scheduler.rb +114 -0
- data/lib/blender/scheduler/dsl.rb +160 -0
- data/lib/blender/scheduling_strategies/base.rb +30 -0
- data/lib/blender/scheduling_strategies/default.rb +37 -0
- data/lib/blender/scheduling_strategies/per_host.rb +38 -0
- data/lib/blender/scheduling_strategies/per_task.rb +37 -0
- data/lib/blender/tasks/base.rb +72 -0
- data/lib/blender/tasks/ruby.rb +31 -0
- data/lib/blender/tasks/shell_out.rb +30 -0
- data/lib/blender/tasks/ssh.rb +25 -0
- data/lib/blender/timer.rb +54 -0
- data/lib/blender/utils/refinements.rb +45 -0
- data/lib/blender/utils/thread_pool.rb +54 -0
- data/lib/blender/utils/ui.rb +51 -0
- data/lib/blender/version.rb +20 -0
- data/spec/blender/blender_rspec.rb +31 -0
- data/spec/blender/discovery_spec.rb +16 -0
- data/spec/blender/drivers/ssh_multi_spec.rb +16 -0
- data/spec/blender/drivers/ssh_spec.rb +17 -0
- data/spec/blender/dsl_spec.rb +19 -0
- data/spec/blender/event_dispatcher_spec.rb +17 -0
- data/spec/blender/job_spec.rb +42 -0
- data/spec/blender/lock_spec.rb +129 -0
- data/spec/blender/scheduled_job_spec.rb +30 -0
- data/spec/blender/scheduler_spec.rb +140 -0
- data/spec/blender/scheduling_strategies/default_spec.rb +75 -0
- data/spec/blender/utils/refinements_spec.rb +16 -0
- data/spec/blender/utils/thread_pool_spec.rb +16 -0
- data/spec/blender_spec.rb +37 -0
- data/spec/data/example.rb +12 -0
- data/spec/spec_helper.rb +35 -0
- metadata +304 -0
data/blender.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'blender/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'pd-blender'
|
8
|
+
spec.version = Blender::VERSION
|
9
|
+
spec.authors = ['Ranjib Dey']
|
10
|
+
spec.email = ['ranjib@pagerduty.com']
|
11
|
+
spec.summary = %q{A modular orchestration engine}
|
12
|
+
spec.description = %q{Discover hosts, run tasks against them and control their execution order}
|
13
|
+
spec.homepage = 'http://github.com/PagerDuty/blender'
|
14
|
+
spec.license = 'Apache 2'
|
15
|
+
spec.bindir = 'bin'
|
16
|
+
spec.executables = ['blend']
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0")
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.add_dependency 'highline'
|
23
|
+
spec.add_dependency 'thor'
|
24
|
+
spec.add_dependency 'mixlib-shellout'
|
25
|
+
spec.add_dependency 'mixlib-log'
|
26
|
+
spec.add_dependency 'net-ssh'
|
27
|
+
spec.add_dependency 'rufus-scheduler'
|
28
|
+
|
29
|
+
spec.add_development_dependency 'bundler'
|
30
|
+
spec.add_development_dependency 'rake'
|
31
|
+
spec.add_development_dependency 'rspec'
|
32
|
+
spec.add_development_dependency 'rubocop'
|
33
|
+
spec.add_development_dependency 'simplecov'
|
34
|
+
spec.add_development_dependency 'yard'
|
35
|
+
spec.add_development_dependency 'pry'
|
36
|
+
end
|
data/lib/blender.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Ranjib Dey (<ranjib@pagerduty.com>)
|
3
|
+
# Copyright:: Copyright (c) 2014 PagerDuty, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
require 'blender/version'
|
19
|
+
require 'blender/scheduler'
|
20
|
+
require 'blender/log'
|
21
|
+
require 'blender/drivers/shellout'
|
22
|
+
require 'json'
|
23
|
+
require 'blender/configuration'
|
24
|
+
|
25
|
+
# Top level module that holds all blender related libraries under this namespace
|
26
|
+
module Blender
|
27
|
+
# Trigger a blender run. If a block is given, then an object of
|
28
|
+
# Blender::Scheduler is yielded, otherwise the argument is treated as a
|
29
|
+
# command and executed with local shellout driver
|
30
|
+
#
|
31
|
+
# @param name [String] Name of the run
|
32
|
+
#
|
33
|
+
# @return [void]
|
34
|
+
def self.blend(name, config_file = nil)
|
35
|
+
scheduler = Scheduler.new(name)
|
36
|
+
if config_file
|
37
|
+
configure(config_file)
|
38
|
+
end
|
39
|
+
if block_given?
|
40
|
+
yield scheduler
|
41
|
+
else
|
42
|
+
scheduler.task(name)
|
43
|
+
end
|
44
|
+
scheduler.run
|
45
|
+
scheduler
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.configure(file)
|
49
|
+
data = JSON.parse(File.read(file))
|
50
|
+
if data['log_level']
|
51
|
+
Blender::Log.level = data['log_level'].to_sym
|
52
|
+
end
|
53
|
+
if data['log_file']
|
54
|
+
Blender::Log.init(data['log_file'])
|
55
|
+
end
|
56
|
+
if data['load_paths']
|
57
|
+
data['load_paths'].each do |path|
|
58
|
+
$LOAD_PATH.unshift(path)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
if data['scheduler']
|
62
|
+
data['scheduler'].each do |key, value|
|
63
|
+
Blender::Configuration[key.to_sym] = value
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/blender/cli.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Ranjib Dey (<ranjib@pagerduty.com>)
|
3
|
+
# Copyright:: Copyright (c) 2014 PagerDuty, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
require 'thor'
|
19
|
+
require 'blender'
|
20
|
+
require 'blender/timer'
|
21
|
+
|
22
|
+
module Blender
|
23
|
+
class CLI < Thor
|
24
|
+
|
25
|
+
def self.exit_on_failure?
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
default_command :from_file
|
30
|
+
package_name 'Blender'
|
31
|
+
|
32
|
+
desc 'from_file ', 'Run blender job from a file'
|
33
|
+
method_option :file,
|
34
|
+
default: 'Blendfile',
|
35
|
+
type: :string,
|
36
|
+
aliases: '-f'
|
37
|
+
|
38
|
+
method_option :config_file,
|
39
|
+
default: nil,
|
40
|
+
type: :string,
|
41
|
+
aliases: '-c',
|
42
|
+
banner: 'Provide additional configuration via json file'
|
43
|
+
|
44
|
+
method_option :noop,
|
45
|
+
default: false,
|
46
|
+
type: :boolean,
|
47
|
+
aliases: '-n',
|
48
|
+
banner: 'No-op mode, run blender without executing jobs'
|
49
|
+
|
50
|
+
def from_file
|
51
|
+
Configuration[:noop] = options[:noop]
|
52
|
+
des = File.read(options[:file])
|
53
|
+
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(options[:file]), 'lib')))
|
54
|
+
Blender.blend(options[:file], options[:config_file]) do |sch|
|
55
|
+
sch.instance_eval(des, __FILE__, __LINE__)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
desc 'schedule', 'Run blender in daemon mode, with job scheduled in periodic interval'
|
60
|
+
method_option :schedule,
|
61
|
+
default: 'Schedule_it',
|
62
|
+
type: :string,
|
63
|
+
aliases: '-s'
|
64
|
+
def schedule
|
65
|
+
sched = Blender::Timer.new
|
66
|
+
des = File.read(options[:schedule])
|
67
|
+
sched.instance_eval(des, __FILE__, __LINE__)
|
68
|
+
sched.join
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Ranjib Dey (<ranjib@pagerduty.com>)
|
3
|
+
# Copyright:: Copyright (c) 2014 PagerDuty, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
require 'singleton'
|
19
|
+
require 'thread'
|
20
|
+
|
21
|
+
module Blender
|
22
|
+
class Configuration
|
23
|
+
include Singleton
|
24
|
+
|
25
|
+
attr_reader :data, :mutex
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
@data = Hash.new{|h,k| h[k] = Hash.new}
|
29
|
+
@data[:noop] = false
|
30
|
+
@mutex = Mutex.new
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.[]=(key, value)
|
34
|
+
instance.mutex.synchronize do
|
35
|
+
instance.data[key] = value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.[](key)
|
40
|
+
instance.mutex.synchronize do
|
41
|
+
instance.data[key]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Ranjib Dey (<ranjib@pagerduty.com>)
|
3
|
+
# Copyright:: Copyright (c) 2014 PagerDuty, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
require 'blender/utils/refinements'
|
19
|
+
require 'blender/configuration'
|
20
|
+
|
21
|
+
module Blender
|
22
|
+
module Discovery
|
23
|
+
include Blender::Utils::Refinements
|
24
|
+
|
25
|
+
def build_discovery(type, opts = {})
|
26
|
+
disco_klass = Blender::Discovery.const_get(camelcase(type.to_s).to_sym)
|
27
|
+
disco_opts = Blender::Configuration[type].merge(opts)
|
28
|
+
disco_klass.new(symbolize(disco_opts))
|
29
|
+
end
|
30
|
+
|
31
|
+
def search_with_config(type, opts = {})
|
32
|
+
options = opts.dup
|
33
|
+
search_opts = options.delete(:search)
|
34
|
+
build_discovery(type, options).search(search_opts)
|
35
|
+
end
|
36
|
+
|
37
|
+
def search(type, options = nil)
|
38
|
+
search_with_config(type, search: options)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Ranjib Dey (<ranjib@pagerduty.com>)
|
3
|
+
# Copyright:: Copyright (c) 2014 PagerDuty, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
require 'blender/log'
|
19
|
+
|
20
|
+
module Blender
|
21
|
+
module Driver
|
22
|
+
class Base
|
23
|
+
attr_reader :config, :events, :stdout, :stderr
|
24
|
+
|
25
|
+
ExecOutput = Struct.new(:exitstatus, :stdout, :stderr)
|
26
|
+
|
27
|
+
def initialize(config = {})
|
28
|
+
cfg = config.dup
|
29
|
+
@stdout = cfg.delete(:stdout) || File.open(File::NULL, 'w')
|
30
|
+
@stderr = cfg.delete(:stderr) || File.open(File::NULL, 'w')
|
31
|
+
@events = cfg.delete(:events) or fail 'Events needed'
|
32
|
+
@config = cfg
|
33
|
+
end
|
34
|
+
|
35
|
+
def execute(tasks, hosts)
|
36
|
+
raise RuntimeError, 'this method must be overridden'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Ranjib Dey (<ranjib@pagerduty.com>)
|
3
|
+
# Copyright:: Copyright (c) 2014 PagerDuty, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
module Blender
|
18
|
+
module Driver
|
19
|
+
class Compound
|
20
|
+
def execute(tasks, hosts)
|
21
|
+
hosts.each do |host|
|
22
|
+
tasks.each do |task|
|
23
|
+
task.driver.execute([task], [host])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Ranjib Dey (<ranjib@pagerduty.com>)
|
3
|
+
# Copyright:: Copyright (c) 2014 PagerDuty, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
require 'blender/drivers/base'
|
19
|
+
|
20
|
+
module Blender
|
21
|
+
module Driver
|
22
|
+
class Ruby < Base
|
23
|
+
|
24
|
+
def execute(tasks, hosts)
|
25
|
+
tasks.each do |task|
|
26
|
+
hosts.each do |host|
|
27
|
+
cmd = run_command(task.command, host)
|
28
|
+
if cmd.exitstatus != 0 and !task.metadata[:ignore_failure]
|
29
|
+
raise ExecutionFailed, cmd.stderr
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def run_command(command, host)
|
36
|
+
exit_status = 0
|
37
|
+
err = ''
|
38
|
+
current_stdout = STDOUT.clone
|
39
|
+
current_stderr = STDERR.clone
|
40
|
+
begin
|
41
|
+
STDOUT.reopen(stdout)
|
42
|
+
STDERR.reopen(stderr)
|
43
|
+
command.call(host)
|
44
|
+
rescue StandardError => e
|
45
|
+
err = e.message + "\nBacktrace:" + e.backtrace.join("\n")
|
46
|
+
exit_status = -1
|
47
|
+
ensure
|
48
|
+
STDOUT.reopen(current_stdout)
|
49
|
+
STDERR.reopen(current_stderr)
|
50
|
+
end
|
51
|
+
ExecOutput.new(exit_status, '', err)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Ranjib Dey (<ranjib@pagerduty.com>)
|
3
|
+
# Copyright:: Copyright (c) 2014 PagerDuty, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
require 'mixlib/shellout'
|
19
|
+
|
20
|
+
module Blender
|
21
|
+
module Driver
|
22
|
+
class ShellOut < Base
|
23
|
+
|
24
|
+
def initialize(config = {})
|
25
|
+
@options = {}
|
26
|
+
cfg = config.dup
|
27
|
+
[:user, :group, :cwd, :umask, :returns, :environment, :timeout].each do |key|
|
28
|
+
if cfg.key?(key)
|
29
|
+
@options[key] = cfg.delete(key)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
super(cfg)
|
33
|
+
end
|
34
|
+
|
35
|
+
def execute(tasks, hosts)
|
36
|
+
verify_local_host!(hosts)
|
37
|
+
tasks.each do |task|
|
38
|
+
cmd = run_command(task.command)
|
39
|
+
if cmd.exitstatus != 0 and !task.metadata[:ignore_failure]
|
40
|
+
raise ExecutionFailed, cmd.stderr
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def run_command(command)
|
46
|
+
cmd = Mixlib::ShellOut.new(command, @options)
|
47
|
+
begin
|
48
|
+
cmd.live_stream = stdout
|
49
|
+
cmd.run_command
|
50
|
+
ExecOutput.new(cmd.exitstatus, cmd.stdout, cmd.stderr)
|
51
|
+
rescue Errno::ENOENT => e
|
52
|
+
ExecOutput.new(-1, '', e.message)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def verify_local_host!(hosts)
|
57
|
+
unless hosts.all?{|h|h == 'localhost'}
|
58
|
+
raise UnsupportedFeature, 'This driver does not support any host other than localhost'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|