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
@@ -0,0 +1,64 @@
|
|
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 'timeout'
|
19
|
+
require 'fcntl'
|
20
|
+
|
21
|
+
module Blender
|
22
|
+
module Lock
|
23
|
+
class Flock
|
24
|
+
|
25
|
+
def initialize(name, options)
|
26
|
+
@path = options['path'] || File.join('/tmp', name)
|
27
|
+
@timeout = options[:timeout] || 0
|
28
|
+
@job_name = name
|
29
|
+
@lock_fd = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def acquire
|
33
|
+
@lock_fd = File.open(@path, File::CREAT|File::RDWR, 0644)
|
34
|
+
@lock_fd.fcntl( Fcntl::F_SETFD, @lock_fd.fcntl(Fcntl::F_GETFD, 0) | Fcntl::FD_CLOEXEC )
|
35
|
+
if @timeout > 0
|
36
|
+
Timeout.timeout(@timeout) do
|
37
|
+
@lock_fd.flock(File::LOCK_EX)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
locked = @lock_fd.flock(File::LOCK_NB | File::LOCK_EX)
|
41
|
+
raise LockAcquisitionError, 'Failed to lock file' if locked == false
|
42
|
+
end
|
43
|
+
@lock_fd.write({job: @job_name, pid: Process.pid }.inspect)
|
44
|
+
end
|
45
|
+
|
46
|
+
def release
|
47
|
+
@lock_fd
|
48
|
+
@lock_fd.flock(File::LOCK_UN)
|
49
|
+
@lock_fd.close
|
50
|
+
end
|
51
|
+
|
52
|
+
def with_lock
|
53
|
+
acquire
|
54
|
+
yield if block_given?
|
55
|
+
rescue Timeout::Error => e
|
56
|
+
raise LockAcquisitionError, 'Timeout while waiting for lock acquisition'
|
57
|
+
rescue LockAcquisitionError => e
|
58
|
+
raise e
|
59
|
+
ensure
|
60
|
+
release
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/blender/log.rb
ADDED
@@ -0,0 +1,24 @@
|
|
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/log'
|
19
|
+
|
20
|
+
module Blender
|
21
|
+
class Log
|
22
|
+
extend Mixlib::Log
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,68 @@
|
|
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
|
+
begin
|
19
|
+
require 'rspec'
|
20
|
+
require 'rspec/mocks'
|
21
|
+
rescue LoadError
|
22
|
+
abort 'Blender::RSpec requires RSpec, RSpec::Mocks'
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'blender'
|
26
|
+
require 'blender/rspec/stub_registry'
|
27
|
+
|
28
|
+
module Blender
|
29
|
+
module Discovery
|
30
|
+
alias_method :old_search, :search
|
31
|
+
def search(type, options = nil)
|
32
|
+
stub = Blender::RSpec::StubRegistry.instance.data.detect do |st|
|
33
|
+
st.type == type && st.opts == options
|
34
|
+
end
|
35
|
+
if stub
|
36
|
+
stub.return_value
|
37
|
+
else
|
38
|
+
old_search(type, options)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
class Utils::UI
|
43
|
+
def puts(string)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module RSpec
|
48
|
+
extend self
|
49
|
+
include Blender::Utils::Refinements
|
50
|
+
def stub_search(type, options = nil)
|
51
|
+
StubRegistry.add(type, options)
|
52
|
+
end
|
53
|
+
|
54
|
+
def noop_scheduler_from_file(file)
|
55
|
+
Blender::Configuration[:noop] = true
|
56
|
+
des = File.read(file)
|
57
|
+
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(file), 'lib')))
|
58
|
+
Blender.blend(file) do |sch|
|
59
|
+
sch.lock_options(nil)
|
60
|
+
sch.instance_eval(des, __FILE__, __LINE__)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
RSpec.configure do |config|
|
67
|
+
config.include Blender::RSpec
|
68
|
+
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
|
+
|
20
|
+
module Blender
|
21
|
+
module RSpec
|
22
|
+
class SearchStub
|
23
|
+
attr_reader :type, :opts, :return_value
|
24
|
+
def initialize(type, opts)
|
25
|
+
@type = type
|
26
|
+
@opts = opts
|
27
|
+
end
|
28
|
+
def and_return(value)
|
29
|
+
@return_value = value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
class StubRegistry
|
33
|
+
include Singleton
|
34
|
+
attr_reader :data
|
35
|
+
def initialize
|
36
|
+
@data = []
|
37
|
+
end
|
38
|
+
def self.add(type, opts)
|
39
|
+
obj = SearchStub.new(type, opts)
|
40
|
+
instance.data << obj
|
41
|
+
obj
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,66 @@
|
|
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
|
+
module Blender
|
19
|
+
# A scheduled job encapsulates a blender based job to be executed
|
20
|
+
# at certain interval. Job is specified as a file path, where
|
21
|
+
# the file contains job written in blender's DSL. Job interval can be
|
22
|
+
# specified either via +cron+ or +every+ method
|
23
|
+
#
|
24
|
+
# +Blender::Timer+ object uses +ScheduledJob+ and to execute the job
|
25
|
+
# and Rufus::Scheduler to schedule it
|
26
|
+
class ScheduledJob
|
27
|
+
attr_reader :schedule, :file
|
28
|
+
# create a new instance
|
29
|
+
# @param name [String] name of the job
|
30
|
+
def initialize(name)
|
31
|
+
@name = name
|
32
|
+
@file = name
|
33
|
+
end
|
34
|
+
|
35
|
+
# set the path of the file holding blender job
|
36
|
+
#
|
37
|
+
# @param file [String] path of the blender file
|
38
|
+
def blender_file(file)
|
39
|
+
@file = file
|
40
|
+
end
|
41
|
+
|
42
|
+
# set the job inteval via cron syntax. The value is passed as it is
|
43
|
+
# to rufus scheduler.
|
44
|
+
#
|
45
|
+
# @param line [String] job interval in cron syntax e.g (*/5 * * * *)
|
46
|
+
def cron(line)
|
47
|
+
@schedule = [ __method__, line]
|
48
|
+
end
|
49
|
+
|
50
|
+
# set the job inteval after every specified seconds
|
51
|
+
# to rufus scheduler.
|
52
|
+
#
|
53
|
+
# @param interval [Fixnum] job interval in seconds
|
54
|
+
def every(interval)
|
55
|
+
@schedule = [ __method__, interval]
|
56
|
+
end
|
57
|
+
|
58
|
+
# invoke a blender run based on the +blender_file+
|
59
|
+
def run
|
60
|
+
des = File.read(file)
|
61
|
+
Blender.blend(file) do |sch|
|
62
|
+
sch.instance_eval(des, __FILE__, __LINE__)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,114 @@
|
|
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
|
+
require 'blender/configuration'
|
20
|
+
require 'blender/utils/thread_pool'
|
21
|
+
require 'blender/exceptions'
|
22
|
+
require 'blender/scheduling_strategies/default'
|
23
|
+
require 'blender/scheduling_strategies/per_host'
|
24
|
+
require 'blender/scheduling_strategies/per_task'
|
25
|
+
require 'blender/utils/thread_pool'
|
26
|
+
require 'blender/scheduler/dsl'
|
27
|
+
require 'blender/event_dispatcher'
|
28
|
+
require 'blender/handlers/doc'
|
29
|
+
require 'blender/tasks/base'
|
30
|
+
|
31
|
+
module Blender
|
32
|
+
class Scheduler
|
33
|
+
|
34
|
+
include SchedulerDSL
|
35
|
+
include Lock
|
36
|
+
|
37
|
+
attr_reader :metadata, :name
|
38
|
+
attr_reader :scheduling_strategy
|
39
|
+
attr_reader :events, :tasks
|
40
|
+
attr_reader :lock_properties
|
41
|
+
|
42
|
+
def initialize(name, tasks = [], metadata = {})
|
43
|
+
@name = name
|
44
|
+
@tasks = tasks
|
45
|
+
@metadata = default_metadata.merge(metadata)
|
46
|
+
@events = Blender::EventDispatcher.new
|
47
|
+
events.register(Blender::Handlers::Doc.new)
|
48
|
+
@scheduling_strategy = nil
|
49
|
+
@lock_properties = {driver: 'flock', driver_options: {}}
|
50
|
+
end
|
51
|
+
|
52
|
+
def run
|
53
|
+
@scheduling_strategy ||= SchedulingStrategy::Default.new
|
54
|
+
events.run_started(self)
|
55
|
+
events.job_computation_started(scheduling_strategy)
|
56
|
+
jobs = scheduling_strategy.compute_jobs(@tasks)
|
57
|
+
events.job_computation_finished(self, jobs)
|
58
|
+
lock do
|
59
|
+
if metadata[:concurrency] > 1
|
60
|
+
concurrent_run(jobs)
|
61
|
+
else
|
62
|
+
serial_run(jobs)
|
63
|
+
end
|
64
|
+
events.run_finished(self)
|
65
|
+
jobs
|
66
|
+
end
|
67
|
+
rescue StandardError => e
|
68
|
+
events.run_failed(self, e)
|
69
|
+
raise e
|
70
|
+
end
|
71
|
+
|
72
|
+
def serial_run(jobs)
|
73
|
+
Log.debug('Invoking serial run')
|
74
|
+
jobs.each do |job|
|
75
|
+
run_job(job)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def concurrent_run(jobs)
|
80
|
+
c = metadata[:concurrency]
|
81
|
+
Log.debug("Invoking concurrent run with concurrency:#{c}")
|
82
|
+
pool = Utils::ThreadPool.new(c)
|
83
|
+
jobs.each do |job|
|
84
|
+
pool.add_job do
|
85
|
+
run_job(job)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
pool.run_till_done
|
89
|
+
end
|
90
|
+
|
91
|
+
def run_job(job)
|
92
|
+
events.job_started(job)
|
93
|
+
Log.debug("Running job #{job.name}")
|
94
|
+
job.run
|
95
|
+
events.job_finished(job)
|
96
|
+
rescue StandardError => e
|
97
|
+
events.job_failed(job, e)
|
98
|
+
if metadata[:ignore_failure]
|
99
|
+
Log.warn("Exception: #{e.inspect} was suppressed, ignoring failure")
|
100
|
+
else
|
101
|
+
raise e
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def default_metadata
|
106
|
+
{
|
107
|
+
ignore_failure: false,
|
108
|
+
concurrency: 0,
|
109
|
+
handlers: [],
|
110
|
+
members: []
|
111
|
+
}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,160 @@
|
|
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/exceptions'
|
19
|
+
require 'blender/scheduling_strategies/default'
|
20
|
+
require 'blender/tasks/base'
|
21
|
+
require 'blender/tasks/ruby'
|
22
|
+
require 'blender/tasks/ssh'
|
23
|
+
require 'blender/tasks/shell_out'
|
24
|
+
require 'highline'
|
25
|
+
require 'blender/utils/refinements'
|
26
|
+
require 'blender/drivers/ssh'
|
27
|
+
require 'blender/drivers/ssh_multi'
|
28
|
+
require 'blender/drivers/shellout'
|
29
|
+
require 'blender/drivers/ruby'
|
30
|
+
require 'blender/discovery'
|
31
|
+
require 'blender/handlers/base'
|
32
|
+
require 'blender/lock/flock'
|
33
|
+
|
34
|
+
module Blender
|
35
|
+
module SchedulerDSL
|
36
|
+
include Blender::Utils::Refinements
|
37
|
+
include Blender::Discovery
|
38
|
+
|
39
|
+
def config(type, opts = {})
|
40
|
+
Blender::Configuration[type].merge!(opts)
|
41
|
+
end
|
42
|
+
|
43
|
+
alias :init :config
|
44
|
+
|
45
|
+
def log_level(level)
|
46
|
+
Blender::Log.level = level
|
47
|
+
end
|
48
|
+
|
49
|
+
def ask(msg, echo = false)
|
50
|
+
HighLine.new.ask(msg){|q| q.echo = echo}
|
51
|
+
end
|
52
|
+
|
53
|
+
def driver(type, opts = {})
|
54
|
+
klass_name = camelcase(type.to_s).to_sym
|
55
|
+
config = symbolize(opts.merge(events: events))
|
56
|
+
yield config if block_given?
|
57
|
+
begin
|
58
|
+
Blender::Driver.const_get(klass_name).new(config)
|
59
|
+
rescue NameError => e
|
60
|
+
raise UnknownDriver, e.message
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_handler(handler)
|
65
|
+
events.register(handler)
|
66
|
+
end
|
67
|
+
|
68
|
+
alias :register_handler :add_handler
|
69
|
+
|
70
|
+
def on(event_type, &block)
|
71
|
+
add_handler(
|
72
|
+
Class.new(Handlers::Base) do
|
73
|
+
define_method(event_type) do |*args|
|
74
|
+
block.call(args)
|
75
|
+
end
|
76
|
+
end.new
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
def build_task(name, type)
|
81
|
+
task_klass = Blender::Task.const_get(camelcase(type.to_s).to_sym)
|
82
|
+
driver_klass = Blender::Driver.const_get(camelcase(type.to_s).to_sym)
|
83
|
+
task = task_klass.new(name)
|
84
|
+
task.members(metadata[:members]) unless metadata[:members].empty?
|
85
|
+
task
|
86
|
+
end
|
87
|
+
|
88
|
+
def append_task(type, task)
|
89
|
+
Log.debug("Appended task:#{task.name}")
|
90
|
+
klass = Blender::Driver.const_get(camelcase(type.to_s).to_sym)
|
91
|
+
if task.driver.nil?
|
92
|
+
opts = {}
|
93
|
+
opts.merge!(Blender::Configuration[type]) unless Blender::Configuration[type].empty?
|
94
|
+
opts.merge!(task.driver_opts)
|
95
|
+
task.use_driver(driver(type, opts))
|
96
|
+
end
|
97
|
+
@tasks << task
|
98
|
+
end
|
99
|
+
|
100
|
+
def shell_task(name, &block)
|
101
|
+
task = build_task(name, :shell_out)
|
102
|
+
task.members(['localhost'])
|
103
|
+
task.instance_eval(&block) if block_given?
|
104
|
+
append_task(:shell_out, task)
|
105
|
+
end
|
106
|
+
|
107
|
+
def ruby_task(name, &block)
|
108
|
+
task = build_task(name, :ruby)
|
109
|
+
task.instance_eval(&block) if block_given?
|
110
|
+
append_task(:ruby, task)
|
111
|
+
end
|
112
|
+
|
113
|
+
def ssh_task(name, &block)
|
114
|
+
task = build_task(name, :ssh)
|
115
|
+
task.instance_eval(&block) if block_given?
|
116
|
+
append_task(:ssh, task)
|
117
|
+
end
|
118
|
+
|
119
|
+
def strategy(strategy)
|
120
|
+
klass_name = camelcase(strategy.to_s).to_sym
|
121
|
+
begin
|
122
|
+
@scheduling_strategy = Blender::SchedulingStrategy.const_get(klass_name).new
|
123
|
+
@scheduling_strategy.freeze
|
124
|
+
rescue NameError => e
|
125
|
+
raise UnknownSchedulingStrategy, e.message
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def concurrency(value)
|
130
|
+
@metadata[:concurrency] = value
|
131
|
+
end
|
132
|
+
|
133
|
+
def ignore_failure(value)
|
134
|
+
@metadata[:ignore_failure] = value
|
135
|
+
end
|
136
|
+
|
137
|
+
def members(hosts)
|
138
|
+
@metadata[:members] = hosts
|
139
|
+
end
|
140
|
+
|
141
|
+
def lock_options(driver, opts = {})
|
142
|
+
@lock_properties[:driver] = driver
|
143
|
+
@lock_properties[:driver_options].merge!(opts.dup)
|
144
|
+
end
|
145
|
+
|
146
|
+
def lock(opts = {})
|
147
|
+
options = lock_properties.dup.merge(opts)
|
148
|
+
if options[:driver]
|
149
|
+
lock_klass = Lock.const_get(camelcase(options[:driver]).to_sym)
|
150
|
+
lock_klass.new(name, options[:driver_options]).with_lock do
|
151
|
+
yield if block_given?
|
152
|
+
end
|
153
|
+
else
|
154
|
+
yield if block_given?
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
alias_method :task, :shell_task
|
159
|
+
end
|
160
|
+
end
|