pd-blender 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|