pd-blender 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +6 -2
- data/README.md +41 -7
- data/Rakefile +5 -3
- data/blender.gemspec +1 -0
- data/lib/blender.rb +9 -11
- data/lib/blender/cli.rb +15 -3
- data/lib/blender/configuration.rb +13 -2
- data/lib/blender/drivers/ruby.rb +0 -1
- data/lib/blender/drivers/scp.rb +85 -0
- data/lib/blender/drivers/shellout.rb +1 -4
- data/lib/blender/drivers/ssh.rb +5 -35
- data/lib/blender/drivers/ssh_exec.rb +54 -0
- data/lib/blender/drivers/ssh_multi.rb +3 -34
- data/lib/blender/event_dispatcher.rb +1 -1
- data/lib/blender/handlers/base.rb +7 -0
- data/lib/blender/handlers/doc.rb +0 -1
- data/lib/blender/job.rb +0 -1
- data/lib/blender/lock/flock.rb +3 -7
- data/lib/blender/rspec/stub_registry.rb +2 -0
- data/lib/blender/scheduler.rb +6 -5
- data/lib/blender/scheduler/dsl.rb +17 -0
- data/lib/blender/tasks/base.rb +0 -1
- data/lib/blender/tasks/ruby.rb +0 -1
- data/lib/blender/tasks/scp.rb +41 -0
- data/lib/blender/utils/refinements.rb +1 -1
- data/lib/blender/utils/thread_pool.rb +0 -1
- data/lib/blender/utils/ui.rb +1 -1
- data/lib/blender/version.rb +1 -1
- data/spec/blender/config_spec.rb +34 -0
- data/spec/blender/dsl_spec.rb +31 -0
- data/spec/blender/lock_spec.rb +51 -47
- data/spec/blender/scheduler_spec.rb +8 -0
- data/spec/blender_spec.rb +11 -0
- data/spec/data/example.rb +0 -1
- data/spec/spec_helper.rb +0 -10
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4755355532133b48f4e4525b8a7671206318b470
|
4
|
+
data.tar.gz: 416b8fd419f08c1900b9f08cca300b7703d4ed68
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7822a210909825f27e67a8887083ac2a3521ef3d465efcdcd3e45dd7b61da3551df7ad59fe480dcbe9875ee6bc77261d3ced3a157b744055d02c4e6c336b08e3
|
7
|
+
data.tar.gz: 07c515f06d1ae9d67360e264c95e2be947cac421ca9319fefdbb1c08f26258a468466ff7976fbb9fff7d3bdc5467e28447fdaa5834858116c7edb85457a4d8b6
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# Blender
|
3
3
|
|
4
4
|
Blender is a modular remote command execution framework. Blender provides few basic
|
5
|
-
primitives to automate
|
5
|
+
primitives to automate across server workflows. Workflows can be expressed in plain
|
6
6
|
ruby DSL and executed using the CLI.
|
7
7
|
|
8
8
|
Following is an example of a simple blender script that will update the package
|
@@ -63,6 +63,17 @@ commands over ssh, serf handlers etc) which can ease automating large cluster
|
|
63
63
|
maintenance, multi stage provisioning, establishing cross server feedback
|
64
64
|
loops etc.
|
65
65
|
|
66
|
+
## Installation
|
67
|
+
|
68
|
+
Blender is published as `pd-blender` in rubygems. And you can install it as:
|
69
|
+
```sh
|
70
|
+
gem install pd-blender
|
71
|
+
```
|
72
|
+
Or declare it as a dependency in your Gemfile, if you are using bundler.
|
73
|
+
```ruby
|
74
|
+
gem 'pd-blender'
|
75
|
+
```
|
76
|
+
|
66
77
|
## Concepts
|
67
78
|
|
68
79
|
Blender is composed of two components:
|
@@ -71,7 +82,7 @@ Blender is composed of two components:
|
|
71
82
|
script can have multiple tasks. Tasks are executed using drivers. Tasks can declare their
|
72
83
|
target hosts.
|
73
84
|
|
74
|
-
* **Scheduling
|
85
|
+
* **Scheduling strategy** - Determines the order of task execution across the hosts.
|
75
86
|
Every blender scripts has one and only one scheduling strategy. Scheduling strategies
|
76
87
|
uses the task list as input and produces a list of jobs, to be executed using drivers.
|
77
88
|
|
@@ -115,6 +126,18 @@ can execute `ssh_task`s. Blender core ships with following tasks and drivers:
|
|
115
126
|
end
|
116
127
|
```
|
117
128
|
|
129
|
+
- **scp_task**: download or upload files using scp
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
members ['host1', 'host2', 'host3']
|
133
|
+
scp_upload '/foo/bar' do
|
134
|
+
from '/path/to/remote/file'
|
135
|
+
end
|
136
|
+
scp_download '/foo/bar' do
|
137
|
+
to '/local/path'
|
138
|
+
end
|
139
|
+
```
|
140
|
+
|
118
141
|
As mentioned earlier tasks are executed using drivers. Tasks can declare their preferred driver or
|
119
142
|
Blender will assign a driver to them automatically. Blender will reuse the global driver if its
|
120
143
|
compatible, else it will create one. By default the ```global_driver``` is a ```shell_out``` driver.
|
@@ -125,9 +148,9 @@ specific to their own implementations.
|
|
125
148
|
|
126
149
|
Scheduling strategies are the most crucial part of a blender script. They decide the
|
127
150
|
order of command execution across distributed nodes in blender. Each blender script is
|
128
|
-
|
151
|
+
invoked using one strategy. Consider them as a transformation, where the input is tasks and ouput is
|
129
152
|
jobs. Tasks and job are pretty similar in their structures (both holds command and hosts),
|
130
|
-
except a jobs can hold multiple tasks within them. We'll come to this later, but first,
|
153
|
+
except a jobs can hold multiple tasks within them. We'll come to this later, but first, let's
|
131
154
|
see how the default strategy work.
|
132
155
|
|
133
156
|
- **default strategy**: the default strategy takes the list of declared tasks (and associated members
|
@@ -275,7 +298,7 @@ Following are some examples:
|
|
275
298
|
- **chef**: discover hosts using Chef search
|
276
299
|
|
277
300
|
```ruby
|
278
|
-
require 'blender/
|
301
|
+
require 'blender/discoveries/chef'
|
279
302
|
|
280
303
|
ruby_task 'print host name' do
|
281
304
|
execute do |host|
|
@@ -285,7 +308,7 @@ Following are some examples:
|
|
285
308
|
end
|
286
309
|
```
|
287
310
|
|
288
|
-
## Invoking blender periodially with Rufus
|
311
|
+
## Invoking blender periodially with Rufus scheduler
|
289
312
|
|
290
313
|
Blender is designed to be used as a standalone script that can be invoked on-demand or
|
291
314
|
consumed as a library, i.e. workflows are written in plain Ruby objects and invoked
|
@@ -320,7 +343,18 @@ both per task level as well as globally.
|
|
320
343
|
## Event handlers
|
321
344
|
|
322
345
|
Blender provides an event dispatchment facility (inspired from Chef), where arbitrary logic can
|
323
|
-
be hooked into the event system (e.g. HipChat notification handlers, statsd handlers, etc) and blender
|
346
|
+
be hooked into the event system (e.g. HipChat notification handlers, statsd handlers, etc) and blender
|
347
|
+
will automatically invoke them during key events. As of now, events are available before and after run
|
348
|
+
and per job execution. Event dispatch system is likely to get more elaborate and blender might have
|
349
|
+
few common event handlers (metric, notifications etc) in near future.
|
350
|
+
|
351
|
+
|
352
|
+
## Ancillary projects
|
353
|
+
|
354
|
+
Blender has a few ancillary projects for integration with other systems, following are few of them:
|
355
|
+
- Zookeeper based locking for distributed blender deployments [blender-zk](https://github.com/PagerDuty/blender-zk)
|
356
|
+
- Serf based host discovery and command dispatch [blender-serf](https://github.com/PagerDuty/blender-serf)
|
357
|
+
- Chef based host discovery [blender-chef](https://github.com/PagerDuty/blender-chef)
|
324
358
|
|
325
359
|
## Supported ruby versions
|
326
360
|
|
data/Rakefile
CHANGED
@@ -8,14 +8,16 @@ YARD::Rake::YardocTask.new do |t|
|
|
8
8
|
end
|
9
9
|
|
10
10
|
RSpec::Core::RakeTask.new(:spec) do |t|
|
11
|
-
t.pattern = %w
|
11
|
+
t.pattern = %w(spec/**/*_spec.rb)
|
12
12
|
end
|
13
13
|
|
14
14
|
RSpec::Core::RakeTask.new(:rspec) do |t|
|
15
|
-
t.pattern = %w
|
15
|
+
t.pattern = %w(spec/**/*_rspec.rb)
|
16
16
|
end
|
17
17
|
|
18
18
|
RuboCop::RakeTask.new(:rubocop) do |t|
|
19
|
-
t.patterns = %w
|
19
|
+
t.patterns = %w(Rakefile Gemfile lib/**/*.rb)
|
20
20
|
t.fail_on_error = true
|
21
21
|
end
|
22
|
+
|
23
|
+
task default: [:spec, :rubocop]
|
data/blender.gemspec
CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_dependency 'mixlib-shellout'
|
25
25
|
spec.add_dependency 'mixlib-log'
|
26
26
|
spec.add_dependency 'net-ssh'
|
27
|
+
spec.add_dependency 'net-scp'
|
27
28
|
spec.add_dependency 'rufus-scheduler'
|
28
29
|
|
29
30
|
spec.add_development_dependency 'bundler'
|
data/lib/blender.rb
CHANGED
@@ -29,13 +29,13 @@ module Blender
|
|
29
29
|
# command and executed with local shellout driver
|
30
30
|
#
|
31
31
|
# @param name [String] Name of the run
|
32
|
+
# @param options[Hash] Additional options for scheduler
|
32
33
|
#
|
33
34
|
# @return [void]
|
34
|
-
def self.blend(name,
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
end
|
35
|
+
def self.blend(name, options = {})
|
36
|
+
config_file = options.delete(:config_file)
|
37
|
+
scheduler = Scheduler.new(name, [], options)
|
38
|
+
configure(config_file) if config_file
|
39
39
|
if block_given?
|
40
40
|
yield scheduler
|
41
41
|
else
|
@@ -47,12 +47,10 @@ module Blender
|
|
47
47
|
|
48
48
|
def self.configure(file)
|
49
49
|
data = JSON.parse(File.read(file))
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
Blender::Log.init(data['log_file'])
|
55
|
-
end
|
50
|
+
|
51
|
+
Blender::Log.level = data['log_level'].to_sym if data['log_level']
|
52
|
+
Blender::Log.init(data['log_file']) if data['log_file']
|
53
|
+
|
56
54
|
if data['load_paths']
|
57
55
|
data['load_paths'].each do |path|
|
58
56
|
$LOAD_PATH.unshift(path)
|
data/lib/blender/cli.rb
CHANGED
@@ -21,10 +21,11 @@ require 'blender/timer'
|
|
21
21
|
|
22
22
|
module Blender
|
23
23
|
class CLI < Thor
|
24
|
-
|
25
24
|
def self.exit_on_failure?
|
26
25
|
true
|
27
26
|
end
|
27
|
+
stop_on_unknown_option! :from_file
|
28
|
+
check_unknown_options! except: :from_file
|
28
29
|
|
29
30
|
default_command :from_file
|
30
31
|
package_name 'Blender'
|
@@ -47,11 +48,22 @@ module Blender
|
|
47
48
|
aliases: '-n',
|
48
49
|
banner: 'No-op mode, run blender without executing jobs'
|
49
50
|
|
50
|
-
|
51
|
+
method_option :quiet,
|
52
|
+
default: false,
|
53
|
+
type: :boolean,
|
54
|
+
aliases: '-q',
|
55
|
+
banner: 'Quiet mode. Disable printing running job details'
|
56
|
+
|
57
|
+
def from_file(*args)
|
51
58
|
Configuration[:noop] = options[:noop]
|
59
|
+
Configuration[:arguments] = args
|
52
60
|
des = File.read(options[:file])
|
53
61
|
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(options[:file]), 'lib')))
|
54
|
-
|
62
|
+
scheduler_options = {
|
63
|
+
config_file: options[:config_file],
|
64
|
+
no_doc: options[:quiet]
|
65
|
+
}
|
66
|
+
Blender.blend(options[:file], scheduler_options) do |sch|
|
55
67
|
sch.instance_eval(des, __FILE__, __LINE__)
|
56
68
|
end
|
57
69
|
end
|
@@ -25,9 +25,8 @@ module Blender
|
|
25
25
|
attr_reader :data, :mutex
|
26
26
|
|
27
27
|
def initialize
|
28
|
-
@data = Hash.new{|h,k| h[k] = Hash.new}
|
29
|
-
@data[:noop] = false
|
30
28
|
@mutex = Mutex.new
|
29
|
+
reset!
|
31
30
|
end
|
32
31
|
|
33
32
|
def self.[]=(key, value)
|
@@ -41,5 +40,17 @@ module Blender
|
|
41
40
|
instance.data[key]
|
42
41
|
end
|
43
42
|
end
|
43
|
+
|
44
|
+
def self.reset!
|
45
|
+
instance.mutex.synchronize do
|
46
|
+
instance.reset!
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def reset!
|
51
|
+
@data = Hash.new{|h, k| h[k] = Hash.new}
|
52
|
+
@data[:noop] = false
|
53
|
+
@data[:arguments] = []
|
54
|
+
end
|
44
55
|
end
|
45
56
|
end
|
data/lib/blender/drivers/ruby.rb
CHANGED
@@ -0,0 +1,85 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Ranjib Dey (<ranjib@pagerduty.com>)
|
3
|
+
# Copyright:: Copyright (c) 2015 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 'net/scp'
|
19
|
+
require 'net/ssh'
|
20
|
+
require 'blender/exceptions'
|
21
|
+
require 'blender/drivers/base'
|
22
|
+
|
23
|
+
module Blender
|
24
|
+
module Driver
|
25
|
+
class Scp < Base
|
26
|
+
attr_reader :user
|
27
|
+
|
28
|
+
def initialize(config = {})
|
29
|
+
cfg = config.dup
|
30
|
+
@user = cfg.delete(:user) || ENV['USER']
|
31
|
+
super(cfg)
|
32
|
+
end
|
33
|
+
|
34
|
+
def execute(tasks, hosts)
|
35
|
+
Log.debug("SCP execution tasks [#{Array(tasks).size}]")
|
36
|
+
Log.debug("SCP on hosts [#{hosts.join(",")}]")
|
37
|
+
Array(hosts).each do |host|
|
38
|
+
session = create_session(host)
|
39
|
+
Array(tasks).each do |task|
|
40
|
+
cmd = run_command(task.command, session)
|
41
|
+
if cmd.exitstatus != 0 and !task.metadata[:ignore_failure]
|
42
|
+
raise ExecutionFailed, cmd.stderr
|
43
|
+
end
|
44
|
+
end
|
45
|
+
session.loop
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def run_command(command, session)
|
50
|
+
begin
|
51
|
+
case command.direction
|
52
|
+
when :upload
|
53
|
+
session.scp.upload!(command.source, command.target)
|
54
|
+
ExecOutput.new(0, '', '')
|
55
|
+
when :download
|
56
|
+
session.scp.download!(command.source, command.target)
|
57
|
+
ExecOutput.new(0, '', '')
|
58
|
+
else
|
59
|
+
ExecOutput.new(-1, '' , "Invalid direction. Can be either :upload or :download. Found:'#{command.direction}'")
|
60
|
+
end
|
61
|
+
rescue StandardError => e
|
62
|
+
ExecOutput.new(-1, stdout, e.message + e.backtrace.join("\n"))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def create_session(host)
|
69
|
+
Log.debug("Invoking ssh: #{user}@#{host}")
|
70
|
+
Net::SSH.start(host, user, config)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class ScpUpload < Blender::Driver::Scp
|
75
|
+
end
|
76
|
+
class ScpDownload < Blender::Driver::Scp
|
77
|
+
def run_command(command, session)
|
78
|
+
begin
|
79
|
+
rescue StandardError => e
|
80
|
+
ExecOutput.new(-1, stdout, e.message + e.backtrace.join("\n"))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -20,14 +20,11 @@ require 'mixlib/shellout'
|
|
20
20
|
module Blender
|
21
21
|
module Driver
|
22
22
|
class ShellOut < Base
|
23
|
-
|
24
23
|
def initialize(config = {})
|
25
24
|
@options = {}
|
26
25
|
cfg = config.dup
|
27
26
|
[:user, :group, :cwd, :umask, :returns, :environment, :timeout].each do |key|
|
28
|
-
if cfg.key?(key)
|
29
|
-
@options[key] = cfg.delete(key)
|
30
|
-
end
|
27
|
+
@options[key] = cfg.delete(key) if cfg.key?(key)
|
31
28
|
end
|
32
29
|
super(cfg)
|
33
30
|
end
|
data/lib/blender/drivers/ssh.rb
CHANGED
@@ -18,12 +18,13 @@
|
|
18
18
|
require 'net/ssh'
|
19
19
|
require 'blender/exceptions'
|
20
20
|
require 'blender/drivers/base'
|
21
|
+
require 'blender/drivers/ssh_exec'
|
21
22
|
|
22
23
|
module Blender
|
23
24
|
module Driver
|
24
25
|
class Ssh < Base
|
25
|
-
|
26
26
|
attr_reader :user
|
27
|
+
include SSHExec
|
27
28
|
|
28
29
|
def initialize(config = {})
|
29
30
|
cfg = config.dup
|
@@ -35,7 +36,7 @@ module Blender
|
|
35
36
|
Log.debug("SSH execution tasks [#{Array(tasks).size}]")
|
36
37
|
Log.debug("SSH on hosts [#{hosts.join(",")}]")
|
37
38
|
Array(hosts).each do |host|
|
38
|
-
session =
|
39
|
+
session = create_session(host)
|
39
40
|
Array(tasks).each do |task|
|
40
41
|
cmd = run_command(task.command, session)
|
41
42
|
if cmd.exitstatus != 0 and !task.metadata[:ignore_failure]
|
@@ -47,47 +48,16 @@ module Blender
|
|
47
48
|
end
|
48
49
|
|
49
50
|
def run_command(command, session)
|
50
|
-
|
51
|
-
command = fixup_sudo(command)
|
52
|
-
exit_status = 0
|
53
|
-
channel = session.open_channel do |ch|
|
54
|
-
ch.request_pty
|
55
|
-
ch.exec(command) do |ch, success|
|
56
|
-
unless success
|
57
|
-
Log.debug("Command not found:#{success.inspect}")
|
58
|
-
exit_status = -1
|
59
|
-
end
|
60
|
-
ch.on_data do |c, data|
|
61
|
-
stdout << data
|
62
|
-
if data =~ /^blender sudo password: /
|
63
|
-
c.send_data("#{password}\n")
|
64
|
-
end
|
65
|
-
end
|
66
|
-
ch.on_extended_data do |c, type, data|
|
67
|
-
stderr << data
|
68
|
-
end
|
69
|
-
ch.on_request "exit-status" do |ichannel, data|
|
70
|
-
l = data.read_long
|
71
|
-
exit_status = [exit_status, l].max
|
72
|
-
Log.debug("exit_status:#{exit_status} , data:#{l}")
|
73
|
-
end
|
74
|
-
end
|
75
|
-
Log.debug("Exit(#{exit_status}) Command: '#{command}'")
|
76
|
-
end
|
77
|
-
channel.wait
|
51
|
+
exit_status = remote_exec(command, session)
|
78
52
|
ExecOutput.new(exit_status, stdout, stderr)
|
79
53
|
end
|
80
54
|
|
81
55
|
private
|
82
56
|
|
83
|
-
def
|
57
|
+
def create_session(host)
|
84
58
|
Log.debug("Invoking ssh: #{user}@#{host}")
|
85
59
|
Net::SSH.start(host, user, config)
|
86
60
|
end
|
87
|
-
|
88
|
-
def fixup_sudo(command)
|
89
|
-
command.sub(/^sudo/, 'sudo -p \'blender sudo password: \'')
|
90
|
-
end
|
91
61
|
end
|
92
62
|
end
|
93
63
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Ranjib Dey (<ranjib@pagerduty.com>)
|
3
|
+
# Author:: Smit Shah (<who828@gmail.com>)
|
4
|
+
# Copyright:: Copyright (c) 2014 PagerDuty, Inc.
|
5
|
+
# License:: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
|
19
|
+
require 'blender/log'
|
20
|
+
module Blender::Driver::SSHExec
|
21
|
+
def remote_exec(command, session)
|
22
|
+
password = config[:password]
|
23
|
+
command = fixup_sudo(command)
|
24
|
+
exit_status = 0
|
25
|
+
channel = session.open_channel do |ch|
|
26
|
+
ch.request_pty
|
27
|
+
ch.exec(command) do |ch, success|
|
28
|
+
unless success
|
29
|
+
Blender::Log.debug("Command not found:#{success.inspect}")
|
30
|
+
exit_status = -1
|
31
|
+
end
|
32
|
+
ch.on_data do |c, data|
|
33
|
+
stdout << data
|
34
|
+
c.send_data("#{password}\n") if data =~ /^blender sudo password: /
|
35
|
+
end
|
36
|
+
ch.on_extended_data do |c, type, data|
|
37
|
+
stderr << data
|
38
|
+
end
|
39
|
+
ch.on_request "exit-status" do |ichannel, data|
|
40
|
+
l = data.read_long
|
41
|
+
exit_status = [exit_status, l].max
|
42
|
+
Blender::Log.debug("exit_status:#{exit_status} , data:#{l}")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
Blender::Log.debug("Exit(#{exit_status}) Command: '#{command}'")
|
46
|
+
end
|
47
|
+
channel.wait
|
48
|
+
exit_status
|
49
|
+
end
|
50
|
+
|
51
|
+
def fixup_sudo(command)
|
52
|
+
command.sub(/^sudo/, 'sudo -p \'blender sudo password: \'')
|
53
|
+
end
|
54
|
+
end
|
@@ -18,6 +18,7 @@
|
|
18
18
|
require 'net/ssh'
|
19
19
|
require 'blender/exceptions'
|
20
20
|
require 'blender/drivers/ssh'
|
21
|
+
require 'blender/drivers/ssh_exec'
|
21
22
|
|
22
23
|
module Blender
|
23
24
|
module Driver
|
@@ -26,7 +27,7 @@ module Blender
|
|
26
27
|
def execute(tasks, hosts)
|
27
28
|
Log.debug("SSH execution tasks [#{tasks.size}]")
|
28
29
|
Log.debug("SSH on hosts [#{hosts.join("\n")}]")
|
29
|
-
session =
|
30
|
+
session = create_session(hosts)
|
30
31
|
Array(tasks).each do |task|
|
31
32
|
cmd = run_command(task.command, session)
|
32
33
|
if cmd.exitstatus != 0 and !task.metadata[:ignore_failure]
|
@@ -36,45 +37,13 @@ module Blender
|
|
36
37
|
session.loop
|
37
38
|
end
|
38
39
|
|
39
|
-
def run_command(command, session)
|
40
|
-
password = @config[:password]
|
41
|
-
command = fixup_sudo(command)
|
42
|
-
exit_status = 0
|
43
|
-
channel = session.open_channel do |ch|
|
44
|
-
ch.request_pty
|
45
|
-
ch.exec(command) do |ch, success|
|
46
|
-
unless success
|
47
|
-
Log.debug("Command not found:#{success.inspect}")
|
48
|
-
exit_status = -1
|
49
|
-
end
|
50
|
-
ch.on_data do |c, data|
|
51
|
-
stdout << data
|
52
|
-
if data =~ /^blender sudo password: /
|
53
|
-
c.send_data("#{password}\n")
|
54
|
-
end
|
55
|
-
end
|
56
|
-
ch.on_extended_data do |c, type, data|
|
57
|
-
stderr << data
|
58
|
-
end
|
59
|
-
ch.on_request "exit-status" do |ichannel, data|
|
60
|
-
l = data.read_long
|
61
|
-
exit_status = [exit_status, l].max
|
62
|
-
Log.debug("exit_status:#{exit_status} , data:#{l}")
|
63
|
-
end
|
64
|
-
end
|
65
|
-
Log.debug("Exit(#{exit_status}) Command: '#{command}'")
|
66
|
-
end
|
67
|
-
channel.wait
|
68
|
-
ExecOutput.new(exit_status, stdout, stderr)
|
69
|
-
end
|
70
|
-
|
71
40
|
def concurrency
|
72
41
|
@config[:concurrency]
|
73
42
|
end
|
74
43
|
|
75
44
|
private
|
76
45
|
|
77
|
-
def
|
46
|
+
def create_session(hosts)
|
78
47
|
user = @config[:user] || ENV['USER']
|
79
48
|
ssh_config = { password: @config[:password]}
|
80
49
|
error_handler = lambda do |server|
|
@@ -20,18 +20,25 @@ module Blender
|
|
20
20
|
class Base
|
21
21
|
def run_started(scheduler)
|
22
22
|
end
|
23
|
+
|
23
24
|
def run_finished(scheduler)
|
24
25
|
end
|
26
|
+
|
25
27
|
def run_failed(scheduler, e)
|
26
28
|
end
|
29
|
+
|
27
30
|
def job_computation_started(strategy)
|
28
31
|
end
|
32
|
+
|
29
33
|
def job_computation_finished(strategy, jobs)
|
30
34
|
end
|
35
|
+
|
31
36
|
def job_started(job)
|
32
37
|
end
|
38
|
+
|
33
39
|
def job_finished(job)
|
34
40
|
end
|
41
|
+
|
35
42
|
def job_failed(job, error)
|
36
43
|
end
|
37
44
|
end
|
data/lib/blender/handlers/doc.rb
CHANGED
data/lib/blender/job.rb
CHANGED
data/lib/blender/lock/flock.rb
CHANGED
@@ -21,12 +21,10 @@ require 'fcntl'
|
|
21
21
|
module Blender
|
22
22
|
module Lock
|
23
23
|
class Flock
|
24
|
-
|
25
24
|
def initialize(name, options)
|
26
25
|
@path = options['path'] || File.join('/tmp', name)
|
27
26
|
@timeout = options[:timeout] || 0
|
28
27
|
@job_name = name
|
29
|
-
@lock_fd = nil
|
30
28
|
end
|
31
29
|
|
32
30
|
def acquire
|
@@ -38,15 +36,15 @@ module Blender
|
|
38
36
|
end
|
39
37
|
else
|
40
38
|
locked = @lock_fd.flock(File::LOCK_NB | File::LOCK_EX)
|
41
|
-
raise LockAcquisitionError,
|
39
|
+
raise LockAcquisitionError, "Failed to lock file '#{@path}'" if locked == false
|
42
40
|
end
|
43
41
|
@lock_fd.write({job: @job_name, pid: Process.pid }.inspect)
|
44
42
|
end
|
45
43
|
|
46
44
|
def release
|
47
|
-
@lock_fd
|
48
45
|
@lock_fd.flock(File::LOCK_UN)
|
49
46
|
@lock_fd.close
|
47
|
+
File.delete(@path) if File.exists?(@path)
|
50
48
|
end
|
51
49
|
|
52
50
|
def with_lock
|
@@ -54,10 +52,8 @@ module Blender
|
|
54
52
|
yield if block_given?
|
55
53
|
rescue Timeout::Error => e
|
56
54
|
raise LockAcquisitionError, 'Timeout while waiting for lock acquisition'
|
57
|
-
rescue LockAcquisitionError => e
|
58
|
-
raise e
|
59
55
|
ensure
|
60
|
-
release
|
56
|
+
release if @lock_fd
|
61
57
|
end
|
62
58
|
end
|
63
59
|
end
|
@@ -25,6 +25,7 @@ module Blender
|
|
25
25
|
@type = type
|
26
26
|
@opts = opts
|
27
27
|
end
|
28
|
+
|
28
29
|
def and_return(value)
|
29
30
|
@return_value = value
|
30
31
|
end
|
@@ -35,6 +36,7 @@ module Blender
|
|
35
36
|
def initialize
|
36
37
|
@data = []
|
37
38
|
end
|
39
|
+
|
38
40
|
def self.add(type, opts)
|
39
41
|
obj = SearchStub.new(type, opts)
|
40
42
|
instance.data << obj
|
data/lib/blender/scheduler.rb
CHANGED
@@ -30,7 +30,6 @@ require 'blender/tasks/base'
|
|
30
30
|
|
31
31
|
module Blender
|
32
32
|
class Scheduler
|
33
|
-
|
34
33
|
include SchedulerDSL
|
35
34
|
include Lock
|
36
35
|
|
@@ -39,14 +38,16 @@ module Blender
|
|
39
38
|
attr_reader :events, :tasks
|
40
39
|
attr_reader :lock_properties
|
41
40
|
|
42
|
-
def initialize(name, tasks = [],
|
41
|
+
def initialize(name, tasks = [], options = {})
|
43
42
|
@name = name
|
44
43
|
@tasks = tasks
|
45
|
-
@metadata = default_metadata.merge(metadata)
|
46
44
|
@events = Blender::EventDispatcher.new
|
47
|
-
|
45
|
+
unless options.delete(:no_doc)
|
46
|
+
events.register(Blender::Handlers::Doc.new)
|
47
|
+
end
|
48
|
+
@metadata = default_metadata.merge(options)
|
48
49
|
@scheduling_strategy = nil
|
49
|
-
@lock_properties = {driver:
|
50
|
+
@lock_properties = {driver: nil, driver_options: {}}
|
50
51
|
end
|
51
52
|
|
52
53
|
def run
|
@@ -21,12 +21,15 @@ require 'blender/tasks/base'
|
|
21
21
|
require 'blender/tasks/ruby'
|
22
22
|
require 'blender/tasks/ssh'
|
23
23
|
require 'blender/tasks/shell_out'
|
24
|
+
require 'blender/tasks/scp'
|
24
25
|
require 'highline'
|
25
26
|
require 'blender/utils/refinements'
|
26
27
|
require 'blender/drivers/ssh'
|
28
|
+
require 'blender/drivers/ssh'
|
27
29
|
require 'blender/drivers/ssh_multi'
|
28
30
|
require 'blender/drivers/shellout'
|
29
31
|
require 'blender/drivers/ruby'
|
32
|
+
require 'blender/drivers/scp'
|
30
33
|
require 'blender/discovery'
|
31
34
|
require 'blender/handlers/base'
|
32
35
|
require 'blender/lock/flock'
|
@@ -116,6 +119,20 @@ module Blender
|
|
116
119
|
append_task(:ssh, task)
|
117
120
|
end
|
118
121
|
|
122
|
+
def scp_upload(name, &block)
|
123
|
+
task = build_task(name, :scp)
|
124
|
+
task.instance_eval(&block) if block_given?
|
125
|
+
task.direction = :upload
|
126
|
+
append_task(:scp, task)
|
127
|
+
end
|
128
|
+
|
129
|
+
def scp_download(name, &block)
|
130
|
+
task = build_task(name, :scp)
|
131
|
+
task.instance_eval(&block) if block_given?
|
132
|
+
task.direction = :download
|
133
|
+
append_task(:scp, task)
|
134
|
+
end
|
135
|
+
|
119
136
|
def strategy(strategy)
|
120
137
|
klass_name = camelcase(strategy.to_s).to_sym
|
121
138
|
begin
|
data/lib/blender/tasks/base.rb
CHANGED
data/lib/blender/tasks/ruby.rb
CHANGED
@@ -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/tasks/ssh'
|
19
|
+
require 'forwardable'
|
20
|
+
|
21
|
+
module Blender
|
22
|
+
module Task
|
23
|
+
class Scp < Blender::Task::Base
|
24
|
+
extend Forwardable
|
25
|
+
def_delegators :@command, :direction, :direction=
|
26
|
+
def initialize(name, metadata = {})
|
27
|
+
super
|
28
|
+
@command = Struct.new(:direction, :source, :target).new
|
29
|
+
@command.target = name
|
30
|
+
@command.source = name
|
31
|
+
@direction = :upload
|
32
|
+
end
|
33
|
+
def from(source)
|
34
|
+
@command.source = source
|
35
|
+
end
|
36
|
+
def to(target)
|
37
|
+
@command.target = target
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/blender/utils/ui.rb
CHANGED
@@ -24,6 +24,7 @@ module Blender
|
|
24
24
|
@mutex = Mutex.new
|
25
25
|
@highline = HighLine.new
|
26
26
|
end
|
27
|
+
|
27
28
|
def puts(string)
|
28
29
|
@mutex.synchronize do
|
29
30
|
$stdout.puts(string)
|
@@ -45,7 +46,6 @@ module Blender
|
|
45
46
|
def color(string, *colors)
|
46
47
|
@highline.color(string, *colors)
|
47
48
|
end
|
48
|
-
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
data/lib/blender/version.rb
CHANGED
@@ -0,0 +1,34 @@
|
|
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 'spec_helper'
|
19
|
+
|
20
|
+
describe Blender::Configuration do
|
21
|
+
it 'should populate argments and noop key' do
|
22
|
+
expect(Blender::Configuration[:noop]).to be(false)
|
23
|
+
expect(Blender::Configuration[:argments]).to be_empty
|
24
|
+
end
|
25
|
+
it 'should set configs globally' do
|
26
|
+
Blender::Configuration[:x] = 1
|
27
|
+
expect(Blender::Configuration[:x]).to eq(1)
|
28
|
+
end
|
29
|
+
it 'should reset config' do
|
30
|
+
Blender::Configuration[:x] = 1
|
31
|
+
Blender::Configuration.reset!
|
32
|
+
expect(Blender::Configuration[:x]).to be_empty
|
33
|
+
end
|
34
|
+
end
|
data/spec/blender/dsl_spec.rb
CHANGED
@@ -16,4 +16,35 @@ describe '#dsl' do
|
|
16
16
|
allow_any_instance_of(Blender::Driver::Ssh).to receive(:execute)
|
17
17
|
scheduler.run
|
18
18
|
end
|
19
|
+
|
20
|
+
context '#scp' do
|
21
|
+
it '#upload' do
|
22
|
+
session = double('Net::SSH::Session', loop: true)
|
23
|
+
scp = double('Net::SSH::Scp')
|
24
|
+
expect(Net::SSH).to receive(:start).with('host1', 'x', password: 'y').and_return(session)
|
25
|
+
expect(session).to receive(:scp).and_return(scp)
|
26
|
+
expect(scp).to receive(:upload!).with('/local/path', '/remote/path')
|
27
|
+
Blender.blend('test') do |sched|
|
28
|
+
sched.config(:scp, user: 'x', password: 'y')
|
29
|
+
sched.members(['host1'])
|
30
|
+
sched.scp_upload '/remote/path' do
|
31
|
+
from '/local/path'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
it '#download' do
|
36
|
+
session = double('Net::SSH::Session', loop: true)
|
37
|
+
scp = double('Net::SSH::Scp')
|
38
|
+
expect(Net::SSH).to receive(:start).with('host1', 'x', password: 'y').and_return(session)
|
39
|
+
expect(session).to receive(:scp).and_return(scp)
|
40
|
+
expect(scp).to receive(:download!).with('/remote/path', '/local/path')
|
41
|
+
Blender.blend('test') do |sched|
|
42
|
+
sched.members(['host1'])
|
43
|
+
sched.config(:scp, user: 'x', password: 'y')
|
44
|
+
sched.scp_download '/remote/path' do
|
45
|
+
to '/local/path'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
19
50
|
end
|
data/spec/blender/lock_spec.rb
CHANGED
@@ -1,72 +1,75 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Blender::Lock do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
4
|
+
context 'File based locking' do
|
5
|
+
it 'should not allow two blender run with same lockfile to run at the same time' do
|
6
|
+
pid1 = fork do
|
7
|
+
Blender.blend('test-1') do |sched|
|
8
|
+
sched.lock_options('flock')
|
9
|
+
sched.members(['localhost'])
|
10
|
+
sched.ruby_task('date') do
|
11
|
+
execute do
|
12
|
+
sleep 5
|
13
|
+
puts 'This will succeed'
|
14
|
+
end
|
14
15
|
end
|
15
16
|
end
|
16
17
|
end
|
17
|
-
end
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
pid2 = fork do
|
20
|
+
STDERR.reopen(File::NULL)
|
21
|
+
Blender.blend('test-1') do |sched|
|
22
|
+
sched.lock_options('flock')
|
23
|
+
sched.members(['localhost'])
|
24
|
+
sched.ruby_task('date') do
|
25
|
+
execute do
|
26
|
+
puts 'This will fail'
|
27
|
+
end
|
26
28
|
end
|
27
29
|
end
|
28
30
|
end
|
31
|
+
|
32
|
+
status1 = Process.wait2 pid1
|
33
|
+
status2 = Process.wait2 pid2
|
34
|
+
expect(status1.last.exitstatus).to eq(0)
|
35
|
+
expect(status2.last.exitstatus).to_not eq(0)
|
29
36
|
end
|
30
|
-
status1 = Process.wait2 pid1
|
31
|
-
status2 = Process.wait2 pid2
|
32
|
-
expect(status1.last.exitstatus).to eq(0)
|
33
|
-
expect(status2.last.exitstatus).to_not eq(0)
|
34
|
-
end
|
35
37
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
38
|
+
it 'should allow two blender run with different lock file to run at the same time' do
|
39
|
+
pid1 = fork do
|
40
|
+
Blender.blend('test-1') do |sched|
|
41
|
+
sched.lock_options('flock')
|
42
|
+
sched.members(['localhost'])
|
43
|
+
sched.ruby_task('date') do
|
44
|
+
execute do
|
45
|
+
sleep 5
|
46
|
+
puts 'This will succeed'
|
47
|
+
end
|
44
48
|
end
|
45
49
|
end
|
46
50
|
end
|
47
|
-
end
|
48
51
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
pid2 = fork do
|
53
|
+
Blender.blend('test-2') do |sched|
|
54
|
+
sched.lock_options('flock')
|
55
|
+
sched.members(['localhost'])
|
56
|
+
sched.ruby_task('date') do
|
57
|
+
execute do
|
58
|
+
puts 'This will succeed'
|
59
|
+
end
|
55
60
|
end
|
56
61
|
end
|
57
62
|
end
|
63
|
+
status1 = Process.wait2 pid1
|
64
|
+
status2 = Process.wait2 pid2
|
65
|
+
expect(status1.last.exitstatus).to eq(0)
|
66
|
+
expect(status2.last.exitstatus).to eq(0)
|
58
67
|
end
|
59
|
-
status1 = Process.wait2 pid1
|
60
|
-
status2 = Process.wait2 pid2
|
61
|
-
expect(status1.last.exitstatus).to eq(0)
|
62
|
-
expect(status2.last.exitstatus).to eq(0)
|
63
|
-
end
|
64
|
-
|
65
|
-
context 'File based locking with timeout' do
|
66
68
|
|
67
69
|
it 'should raise lock acquisition error when times out' do
|
68
70
|
pid1 = fork do
|
69
71
|
Blender.blend('test-1') do |sched|
|
72
|
+
sched.lock_options('flock')
|
70
73
|
sched.members(['localhost'])
|
71
74
|
sched.ruby_task('date') do
|
72
75
|
execute do
|
@@ -98,6 +101,7 @@ describe Blender::Lock do
|
|
98
101
|
it 'should not raise lock acquisition error when able to acquire lock within timeout period' do
|
99
102
|
pid1 = fork do
|
100
103
|
Blender.blend('test-1') do |sched|
|
104
|
+
sched.lock_options('flock')
|
101
105
|
sched.members(['localhost'])
|
102
106
|
sched.ruby_task('date') do
|
103
107
|
execute do
|
@@ -112,7 +116,7 @@ describe Blender::Lock do
|
|
112
116
|
STDERR.reopen(File::NULL)
|
113
117
|
Blender.blend('test-1') do |sched|
|
114
118
|
sched.members(['localhost'])
|
115
|
-
sched.lock_options('flock', timeout:
|
119
|
+
sched.lock_options('flock', timeout: 10)
|
116
120
|
sched.ruby_task('date') do
|
117
121
|
execute do
|
118
122
|
puts 'This will fail'
|
@@ -123,7 +127,7 @@ describe Blender::Lock do
|
|
123
127
|
status1 = Process.wait2 pid1
|
124
128
|
status2 = Process.wait2 pid2
|
125
129
|
expect(status1.last.exitstatus).to eq(0)
|
126
|
-
expect(status2.last.exitstatus).
|
130
|
+
expect(status2.last.exitstatus).to eq(0)
|
127
131
|
end
|
128
132
|
end
|
129
133
|
end
|
@@ -4,6 +4,14 @@ describe Blender::Scheduler do
|
|
4
4
|
let(:scheduler) do
|
5
5
|
described_class.new('test')
|
6
6
|
end
|
7
|
+
describe '#no_doc' do
|
8
|
+
it 'should not use document handler if no_doc option is passed' do
|
9
|
+
expect(Blender::Handlers::Doc).to_not receive(:new)
|
10
|
+
task = Blender::Task::Ruby.new('test')
|
11
|
+
sched = described_class.new('no_doc', [] , no_doc: true)
|
12
|
+
sched.run
|
13
|
+
end
|
14
|
+
end
|
7
15
|
describe '#DSL' do
|
8
16
|
subject(:task){scheduler.tasks.first}
|
9
17
|
it '#ask' do
|
data/spec/blender_spec.rb
CHANGED
@@ -16,6 +16,7 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
|
18
18
|
require 'spec_helper'
|
19
|
+
require 'blender/cli'
|
19
20
|
|
20
21
|
describe Blender do
|
21
22
|
describe '#blend' do
|
@@ -33,5 +34,15 @@ describe Blender do
|
|
33
34
|
end
|
34
35
|
expect(x).to eq(100)
|
35
36
|
end
|
37
|
+
context 'CLI' do
|
38
|
+
it 'should store additional arguments in config' do
|
39
|
+
Blender::CLI.start(%w{-f spec/data/example.rb -x -y -z})
|
40
|
+
expect(Blender::Configuration[:arguments]).to eq(%w{-x -y -z})
|
41
|
+
end
|
42
|
+
it 'should store additional arguments in config' do
|
43
|
+
expect(Blender::Handlers::Doc).to_not receive(:new)
|
44
|
+
Blender::CLI.start(%w{-q -f spec/data/example.rb})
|
45
|
+
end
|
46
|
+
end
|
36
47
|
end
|
37
48
|
end
|
data/spec/data/example.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,9 +1,3 @@
|
|
1
|
-
require 'simplecov'
|
2
|
-
SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
|
3
|
-
SimpleCov.start do
|
4
|
-
add_filter '/spec/'
|
5
|
-
add_filter '.bundle'
|
6
|
-
end
|
7
1
|
|
8
2
|
require 'rspec'
|
9
3
|
require 'rspec/mocks'
|
@@ -27,9 +21,5 @@ RSpec.configure do |config|
|
|
27
21
|
config.mock_with :rspec do |mocks|
|
28
22
|
mocks.verify_doubled_constant_names = true
|
29
23
|
end
|
30
|
-
config.before(:each) do
|
31
|
-
doc = double(Blender::Handlers::Doc).as_null_object
|
32
|
-
allow(Blender::Handlers::Doc).to receive(:new).and_return(doc)
|
33
|
-
end
|
34
24
|
config.backtrace_exclusion_patterns = []
|
35
25
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pd-blender
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ranjib Dey
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-02-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: highline
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: net-scp
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: rufus-scheduler
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -216,8 +230,10 @@ files:
|
|
216
230
|
- lib/blender/drivers/base.rb
|
217
231
|
- lib/blender/drivers/compound.rb
|
218
232
|
- lib/blender/drivers/ruby.rb
|
233
|
+
- lib/blender/drivers/scp.rb
|
219
234
|
- lib/blender/drivers/shellout.rb
|
220
235
|
- lib/blender/drivers/ssh.rb
|
236
|
+
- lib/blender/drivers/ssh_exec.rb
|
221
237
|
- lib/blender/drivers/ssh_multi.rb
|
222
238
|
- lib/blender/event_dispatcher.rb
|
223
239
|
- lib/blender/exceptions.rb
|
@@ -237,6 +253,7 @@ files:
|
|
237
253
|
- lib/blender/scheduling_strategies/per_task.rb
|
238
254
|
- lib/blender/tasks/base.rb
|
239
255
|
- lib/blender/tasks/ruby.rb
|
256
|
+
- lib/blender/tasks/scp.rb
|
240
257
|
- lib/blender/tasks/shell_out.rb
|
241
258
|
- lib/blender/tasks/ssh.rb
|
242
259
|
- lib/blender/timer.rb
|
@@ -245,6 +262,7 @@ files:
|
|
245
262
|
- lib/blender/utils/ui.rb
|
246
263
|
- lib/blender/version.rb
|
247
264
|
- spec/blender/blender_rspec.rb
|
265
|
+
- spec/blender/config_spec.rb
|
248
266
|
- spec/blender/discovery_spec.rb
|
249
267
|
- spec/blender/drivers/ssh_multi_spec.rb
|
250
268
|
- spec/blender/drivers/ssh_spec.rb
|
@@ -286,6 +304,7 @@ specification_version: 4
|
|
286
304
|
summary: A modular orchestration engine
|
287
305
|
test_files:
|
288
306
|
- spec/blender/blender_rspec.rb
|
307
|
+
- spec/blender/config_spec.rb
|
289
308
|
- spec/blender/discovery_spec.rb
|
290
309
|
- spec/blender/drivers/ssh_multi_spec.rb
|
291
310
|
- spec/blender/drivers/ssh_spec.rb
|