nickstenning-outback 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ # Outback - a transactional shell runner
2
+
3
+ Outback is a simple ruby library for running transactional pairs of shell commands ("rollout/rollback") in a known sequence. It's useful for running a set of dependent commands one after another with the ability to stop, find out what's gone wrong, and rollback all the steps so far if something goes awry.
@@ -0,0 +1,9 @@
1
+ require 'rake'
2
+ require 'spec/rake/spectask'
3
+
4
+ desc "Run all examples with RCov"
5
+ Spec::Rake::SpecTask.new('rcov_specs') do |t|
6
+ t.spec_files = FileList['spec/**/*_spec.rb']
7
+ t.rcov = true
8
+ t.rcov_opts = ['--exclude', 'spec']
9
+ end
@@ -1,11 +1,10 @@
1
1
  require 'outback/manager'
2
- require 'outback/shelltask'
3
- require 'outback/yaml'
2
+ require 'outback/task_helper'
3
+ require 'outback/task'
4
4
 
5
5
  module Outback
6
6
 
7
7
  class Error < RuntimeError; end
8
8
  class TransactionError < Error; end
9
- class DuplicateNamedTaskError < Error; end
10
9
 
11
10
  end
@@ -1,104 +1,94 @@
1
1
  require 'yaml'
2
+ require 'observer'
2
3
 
3
4
  module Outback
4
5
 
5
6
  class Manager
6
7
 
8
+ include Observable
9
+
7
10
  ROLLOUT = 1
8
11
  ROLLBACK = -1
9
12
 
10
- attr_reader :tasks, :position
11
- attr_accessor :workdir
12
- attr_writer :watcher
13
+ attr_reader :tasks
14
+ attr_accessor :direction, :position, :workdir, :cache
13
15
 
14
16
  def initialize
15
17
  @tasks = []
16
- @names = {}
18
+ @cache = {}
17
19
  @position = 0
20
+ @direction = 1
18
21
  end
19
22
 
20
23
  def add_tasks( *tasks )
21
- [*tasks].each do |task|
22
- task.workdir = @workdir unless task.workdir
23
-
24
- if task.name
25
- if @names.has_key?(task.name)
26
- raise DuplicateNamedTaskError, "Cannot add a named task more than once!"
27
- else
28
- @names[task.name] = @tasks.length
29
- end
30
- end
31
-
32
- @tasks << task
33
- end
24
+ @tasks += tasks
34
25
  end
35
26
 
36
- alias_method :add_task, :add_tasks
37
-
38
- def find_task( name )
39
- @tasks[@names[name]]
27
+ def add_task( task=nil, &block )
28
+ if block_given?
29
+ task = Outback::Task.new(&block)
30
+ end
31
+ add_tasks( task ) if task
40
32
  end
41
33
 
42
34
  def rollout!
43
35
  @direction = ROLLOUT
44
- run
36
+ run_all
45
37
  end
46
38
 
47
39
  def rollback!
48
40
  @direction = ROLLBACK
49
- run
41
+ run_all
50
42
  end
51
43
 
52
- def rollout_from( task )
53
- @position = @tasks.index(task)
54
- rollout!
44
+ def run_all
45
+ cache.delete(:errors)
46
+
47
+ tasks_to_run.each do |task|
48
+ temp = cache.dup
49
+ begin
50
+ run task, @direction
51
+ rescue => e
52
+ self.cache = temp
53
+ cache[:errors] ||= []
54
+ cache[:errors] << e
55
+ break false
56
+ end
57
+ @position += @direction
58
+ changed
59
+ notify_observers(state, cache)
60
+ end
55
61
  end
56
62
 
57
- def rollback_from( task )
58
- @position = @tasks.index(task) - 1
59
- rollback!
63
+ def tasks_to_run
64
+ if @direction == ROLLOUT
65
+ @tasks[@position..-1]
66
+ elsif @direction == ROLLBACK
67
+ @tasks[0...@position].reverse
68
+ end
60
69
  end
61
70
 
62
- def attempt( task )
63
- method = {ROLLOUT => :rollout!, ROLLBACK => :rollback!}[@direction]
64
- ret = task.send(method)
65
- @watcher.notify(task) if @watcher
66
- return ret
71
+ def state
72
+ { :position => @position,
73
+ :direction => @direction
74
+ }
67
75
  end
68
76
 
69
- def current_task
70
- @tasks[@position]
77
+ def restore_state( state )
78
+ @position = state[:position] || @position
79
+ @direction = state[:direction] || @direction
80
+ self
71
81
  end
82
+
83
+ def run( task, direction=1 )
84
+ method = { ROLLOUT => :rollout!, ROLLBACK => :rollback! }[direction]
72
85
 
73
- private
74
-
75
- def run
76
- if @direction == ROLLOUT
77
- task_list = @tasks[@position..-1]
78
- elsif @direction == ROLLBACK
79
- task_list = @tasks[0..@position + 1].reverse
80
- end
81
- task_list.each do |task|
82
- @position = @tasks.index(task)
83
- unless attempt current_task
84
- fail
85
- break
86
- end
87
- end
86
+ task.send(method, task_helper)
88
87
  end
89
88
 
90
- def fail
91
- case @direction
92
- when ROLLOUT
93
- #rollback_from current_task
94
- raise Error, "Could not rollout task #{current_task}, attempting rollback."
95
- when ROLLBACK
96
- raise TransactionError, "Could not rollback task #{current_task}, aborting."
97
- else
98
- raise Error, "Unknown direction!"
99
- end
89
+ def task_helper
90
+ @task_helper ||= TaskHelper.new(self)
100
91
  end
101
-
102
92
  end
103
93
 
104
- end
94
+ end
@@ -0,0 +1,35 @@
1
+ module Outback
2
+ class Task
3
+ def initialize
4
+ @rollback = proc {}
5
+ @rollout = proc {}
6
+ if block_given?
7
+ yield self
8
+ end
9
+ end
10
+
11
+ def rollout( &block )
12
+ if block_given?
13
+ @rollout = block
14
+ else
15
+ @rollout
16
+ end
17
+ end
18
+
19
+ def rollback( &block )
20
+ if block_given?
21
+ @rollback = block
22
+ else
23
+ @rollback
24
+ end
25
+ end
26
+
27
+ def rollout!( task_helper = nil )
28
+ @rollout.call(task_helper)
29
+ end
30
+
31
+ def rollback!( task_helper = nil )
32
+ @rollback.call(task_helper)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,39 @@
1
+ require 'rubygems'
2
+ require 'open4'
3
+
4
+ module Outback
5
+ class TaskHelper
6
+ def initialize( manager=nil )
7
+ @manager = manager
8
+ end
9
+
10
+ def cache
11
+ if @manager and cache = @manager.cache
12
+ cache
13
+ else
14
+ {}
15
+ end
16
+ end
17
+
18
+ def workdir
19
+ if @manager and workdir = @manager.workdir
20
+ workdir
21
+ else
22
+ Dir.getwd
23
+ end
24
+ end
25
+
26
+ def sys( command )
27
+ result = {}
28
+ Dir.chdir(workdir) do
29
+ status = Open4.popen4(*command) do |pid, i, o, e|
30
+ result[:stdout] = o.read
31
+ result[:stderr] = e.read
32
+ end
33
+ result[:exit_status] = status.exitstatus
34
+ end
35
+ return result
36
+ end
37
+
38
+ end
39
+ end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = %q{outback}
3
- s.version = "0.0.3"
3
+ s.version = "0.0.4"
4
4
 
5
5
  s.specification_version = 2 if s.respond_to? :specification_version=
6
6
 
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
8
8
  s.authors = ["Nick Stenning"]
9
9
  s.date = %q{2008-07-14}
10
10
  s.email = ["nick@whiteink.com"]
11
- s.files = ["lib/outback/manager.rb", "lib/outback/shelltask.rb", "lib/outback/yaml.rb", "lib/outback.rb", "outback.gemspec", "spec/outback/manager_spec.rb", "spec/outback/shelltask_spec.rb", "spec/outback/yaml_spec.rb", "spec/spec_helper.rb"]
11
+ s.files = ["lib/outback/manager.rb", "lib/outback/task.rb", "lib/outback/task_helper.rb", "lib/outback.rb", "outback.gemspec", "Rakefile", "README.markdown", "spec/fixtures/example_manager.rb", "spec/outback/manager_spec.rb", "spec/outback/task_helper_spec.rb", "spec/outback/task_spec.rb", "spec/spec_helper.rb"]
12
12
  s.has_rdoc = true
13
13
  s.homepage = %q{http://github.com/nickstenning/outback}
14
14
  s.require_paths = ["lib"]
@@ -0,0 +1,61 @@
1
+ $: << File.expand_path(File.dirname(__FILE__) + '/../../lib')
2
+ require 'outback'
3
+
4
+ include Outback
5
+
6
+ if $0 == __FILE__
7
+
8
+ @man = Manager.new
9
+
10
+ @man.add_task do |t|
11
+ t.rollout do |m|
12
+ m.cache[:result] = 1
13
+ end
14
+ t.rollback do |m|
15
+ m.cache.delete(:result)
16
+ end
17
+ end
18
+
19
+ @man.add_task do |t|
20
+ t.rollout do |m|
21
+ m.cache[:result] += 2
22
+ end
23
+ t.rollback do |m|
24
+ m.cache[:result] -= 2
25
+ end
26
+ end
27
+
28
+ @man.add_task do |t|
29
+ t.rollout do |m|
30
+ m.cache[:number_three] = m.cache[:result]
31
+ end
32
+ t.rollback do |m|
33
+ m.cache.delete(:number_three)
34
+ end
35
+ end
36
+
37
+ @man.add_task do |t|
38
+ t.rollout do |m|
39
+ m.cache[:b0rk] = "hello"
40
+ end
41
+ t.rollback do |m|
42
+ m.cache.delete(:b0rk)
43
+ end
44
+ end
45
+
46
+ @man.add_task do |t|
47
+ t.rollout do |m|
48
+ m.cache[:syscall] = m.sys("pwd")[:stdout].strip
49
+ end
50
+ t.rollback do |m|
51
+ m.cache.delete(:syscall)
52
+ end
53
+ end
54
+
55
+ @man.rollout!
56
+ y @man.cache
57
+ @man.rollback!
58
+ y @man.cache
59
+
60
+
61
+ end
@@ -5,9 +5,9 @@ describe Outback::Manager do
5
5
 
6
6
  before do
7
7
  @obm = Outback::Manager.new
8
- @task1 = mock("task1", :workdir => "/tmp", :name => "task1", :null_object => true)
9
- @task2 = mock("task2", :workdir => nil, :name => nil, :null_object => true)
10
- @task3 = mock("task3", :workdir => nil, :name => "task3", :null_object => true)
8
+ @task1 = mock(:task1, :null_object => true)
9
+ @task2 = mock(:task2, :null_object => true)
10
+ @task3 = mock(:task3, :null_object => true)
11
11
  end
12
12
 
13
13
  it "should have a list of tasks" do
@@ -22,6 +22,12 @@ describe Outback::Manager do
22
22
  @obm.should have(2).tasks
23
23
  end
24
24
 
25
+ it "should add a new Outback::Task and add it if #add_task is called with a block" do
26
+ @obm.add_task do
27
+ end
28
+ @obm.should have(1).tasks
29
+ end
30
+
25
31
  it "should add multiple tasks to its list, in order, with #add_tasks" do
26
32
  @obm.add_tasks(@task1, @task2, @task3)
27
33
  @obm.should have(3).tasks
@@ -30,20 +36,20 @@ describe Outback::Manager do
30
36
  @obm.tasks[2].should == @task3
31
37
  end
32
38
 
33
- it "should not allow more than one task with the same name" do
34
- lambda { @obm.add_tasks(@task1, @task1) }.should raise_error(Outback::DuplicateNamedTaskError)
35
- lambda { @obm.add_tasks(@task1, @task2, @task1) }.should raise_error(Outback::DuplicateNamedTaskError)
36
- lambda { @obm.add_tasks(@task1, @task3, @task3) }.should raise_error(Outback::DuplicateNamedTaskError)
39
+ it "should give a hash representing its state on #state" do
40
+ @obm.state[:position].should == 0
41
+ @obm.state[:direction].should == 1
37
42
  end
38
43
 
39
- it "should allow the same task twice if unnamed" do
40
- lambda { @obm.add_tasks(@task1, @task2, @task2) }.should_not raise_error
44
+ it "should restore its state when passed in a state hash on #restore_state" do
45
+ @obm.restore_state({:position => 3, :direction => -1})
46
+ @obm.position.should == 3
47
+ @obm.direction.should == -1
41
48
  end
42
49
 
43
- it "should return the named task with #find_task(name)" do
44
- @obm.add_tasks(@task1, @task2, @task3)
45
- @obm.find_task('task1').should == @task1
46
- @obm.find_task('task3').should == @task3
50
+ it "should update its cache with #cache=" do
51
+ @obm.cache = {:foo => "bar"}
52
+ @obm.cache[:foo].should == "bar"
47
53
  end
48
54
 
49
55
  describe "(with a few tasks)" do
@@ -52,67 +58,111 @@ describe Outback::Manager do
52
58
  @obm.add_tasks(@task1, @task2)
53
59
  end
54
60
 
55
- it "should call each task's #rollout! method, in order, on #rollout!" do
56
- @task1.should_receive(:rollout!).and_return(true)
57
- @task2.should_receive(:rollout!).and_return(true)
58
- @obm.rollout!
61
+ it "should, by default, be rolling out" do
62
+ @obm.direction.should == Outback::Manager::ROLLOUT
59
63
  end
60
64
 
61
- it "should call each task's #rollback! method, in reverse order, on #rollback!" do
62
- @task1.should_receive(:rollout!).and_return(true)
63
- @task2.should_receive(:rollout!).and_return(true)
64
- @obm.rollout!
65
+ it "should start at the first task" do
66
+ @obm.position.should == 0
65
67
  end
66
68
 
67
- it "should raise an Outback::Error if a rollout fails" do
68
- @task1.stub!(:rollback!)
69
- @task1.should_receive(:rollout!).and_return(false)
70
- @task2.should_not_receive(:rollout!)
71
- @task2.should_not_receive(:rollback!)
72
- lambda { @obm.rollout! }.should raise_error(Outback::Error)
69
+ it "should report the tasks it will run in any given state with #tasks_to_run" do
70
+ @obm.add_task(@task3)
71
+ @obm.tasks_to_run.should == [@task1, @task2, @task3]
72
+ @obm.direction = -1
73
+ @obm.tasks_to_run.should be_empty
74
+ @obm.position = 3
75
+ @obm.tasks_to_run.should == [@task3, @task2, @task1]
76
+ @obm.position = 2
77
+ @obm.tasks_to_run.should == [@task2, @task1]
78
+ @obm.direction = 1
79
+ @obm.tasks_to_run.should == [@task3]
80
+ end
81
+
82
+ it "should call each task's #rollout! method, in order, on #rollout!" do
83
+ @task1.should_receive(:rollout!)
84
+ @task2.should_receive(:rollout!)
85
+ @obm.rollout!.should == [@task1, @task2]
73
86
  end
74
87
 
75
- it "should raise an Outback::TransactionError if a rollback subsequently fails" do
76
- @task2.should_receive(:rollback!).and_return(false)
88
+ it "should not roll back any tasks if it isn't already rolled out" do
77
89
  @task1.should_not_receive(:rollback!)
78
- lambda { @obm.rollback! }.should raise_error(Outback::TransactionError)
90
+ @task2.should_not_receive(:rollback!)
91
+ @obm.rollback!.should be_empty
79
92
  end
80
93
 
81
- end
82
-
83
- describe "(with #workdir set)" do
94
+ it "should halt the rollout if any task's #rollout! method throws an error, and put the error in the cache" do
95
+ @error_task = mock(:error_task, :null_object => true)
96
+ @error_task.should_receive(:rollout!).and_raise("Just some runtime error.")
97
+ @obm.add_tasks(@error_task, @task2)
98
+ @obm.rollout!.should_not be_true
99
+ @obm.cache.should have(1).error
100
+ @obm.tasks_to_run.should == [@error_task, @task2]
101
+ end
84
102
 
85
- before do
86
- @task1 = Outback::ShellTask.new("", "")
87
- @task1.workdir = "/tmp"
88
- @task2 = Outback::ShellTask.new("", "")
89
- @obm.workdir = "/nonexistent"
90
- @obm.add_tasks(@task1, @task2)
103
+ it "should clear the cache errors before every rollout or rollback" do
104
+ @obm.cache[:errors] = "I'm an error."
105
+ @obm.rollout!
106
+ @obm.cache[:errors].should be_nil
107
+ @obm.cache[:errors] = "I'm an error."
108
+ @obm.rollback!
109
+ @obm.cache[:errors].should be_nil
110
+ end
111
+
112
+
113
+ it "should run each task in a transactional manner, so changes to the cache that are followed by an error are reversed" do
114
+ @error_task = Outback::Task.new do |t|
115
+ t.rollout do |m|
116
+ m.cache[:result] = "Foo"
117
+ raise "Some cockup!"
118
+ end
119
+ end
120
+
121
+ @obm.add_tasks(@error_task, @task2)
122
+ @obm.rollout!.should_not be_true
123
+ @obm.cache[:result].should be_nil
124
+ end
125
+
126
+ it "should call each task's #rollback! method, in reverse order, on #rollback!, provided it's already rolled out" do
127
+ @obm.position = 2
128
+ @task2.should_receive(:rollback!)
129
+ @task1.should_receive(:rollback!)
130
+ @obm.rollback!.should == [@task2, @task1]
91
131
  end
92
132
 
93
- it "should set the workdir of each of its tasks if it is currently nil" do
94
- @task1.workdir.should == "/tmp"
95
- @task2.workdir.should == "/nonexistent"
133
+ it "should not roll out any tasks if it isn't already rolled back" do
134
+ @obm.position = 2
135
+ @task1.should_not_receive(:rollout!)
136
+ @task2.should_not_receive(:rollout!)
137
+ @obm.rollout!.should be_empty
138
+ end
139
+
140
+ it "should pass an instance of TaskHelper into tasks' #rollout! and #rollback! methods" do
141
+ th = @obm.task_helper
142
+ @task1.should_receive(:rollout!).with(th)
143
+ @obm.rollout!
96
144
  end
97
145
 
98
146
  end
99
147
 
100
- describe "(with #watcher set)" do
148
+ describe "(with observers)" do
101
149
 
102
150
  before do
103
- @task1 = Outback::ShellTask.new("pwd", "")
104
- @task2 = Outback::ShellTask.new("echo 'hello'", "")
105
- require 'tempfile'
106
- @obm.workdir = Dir.tmpdir
107
151
  @obm.add_tasks(@task1, @task2)
108
- @watcher = mock("watcher")
109
- @obm.watcher = @watcher
152
+
153
+ @watcher = mock(:watcher)
154
+ @watcher.stub!(:update)
155
+
156
+ @obm.add_observer(@watcher)
110
157
  end
111
158
 
112
- it "should call the watcher's #notify method after every task rollout/rollback with the task as an argument" do
113
- @watcher.should_receive(:notify).ordered.with(@task1)
114
- @watcher.should_receive(:notify).ordered.with(@task2)
159
+ it "should call the watcher's #update method after each rollout or rollback with a state hash and the results cache as parameters." do
160
+ @watcher.should_receive(:update).ordered.with({:position => 1, :direction => 1}, {})
161
+ @watcher.should_receive(:update).ordered.with({:position => 2, :direction => 1}, {})
162
+ @watcher.should_receive(:update).ordered.with({:position => 1, :direction => -1}, {})
163
+ @watcher.should_receive(:update).ordered.with({:position => 0, :direction => -1}, {})
115
164
  @obm.rollout!
165
+ @obm.rollback!
116
166
  end
117
167
 
118
168
  end
@@ -0,0 +1,46 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Outback::TaskHelper do
4
+
5
+ before do
6
+ @th = Outback::TaskHelper.new(@manager)
7
+ end
8
+
9
+ it "should give an empty hash on #cache" do
10
+ @th.cache.should be_a_kind_of(Hash)
11
+ @th.cache.should be_empty
12
+ end
13
+
14
+ it "should give the current working directory on #workdir" do
15
+ @th.workdir.should == Dir.getwd
16
+ end
17
+
18
+ it "should provide a #sys method which makes a system call, returning a hash with stdout, stderr and exit_status" do
19
+ result = @th.sys("pwd")
20
+ result[:stdout].should == Dir.getwd + "\n"
21
+ result[:stderr].should == ""
22
+ result[:exit_status].should == 0
23
+ end
24
+
25
+ describe "(with a manager set)" do
26
+ before do
27
+ @cache = {}
28
+ @manager = mock(:manager, :cache => @cache, :workdir => '/myworkdir')
29
+ @th = Outback::TaskHelper.new(@manager)
30
+ end
31
+
32
+ it "should give the manager's cache on #cache" do
33
+ @th.cache.should == @cache
34
+ end
35
+
36
+ it "should give the manager's workdir on #workdir" do
37
+ @th.workdir.should == '/myworkdir'
38
+ end
39
+
40
+ it "should execute calls to sys in the workdir" do
41
+ @manager.should_receive(:workdir).and_return("/")
42
+ lambda { @result = @th.sys("pwd") }.should_not raise_error
43
+ @result[:stdout].should == "/\n"
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,41 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Outback::Task do
4
+
5
+ before do
6
+ @task = Outback::Task.new
7
+ end
8
+
9
+ it "should take an optional block on its constructor, passing itself in as the block parameter" do
10
+ lambda { @task = Outback::Task.new }.should_not raise_error
11
+ temp = nil
12
+ @task = Outback::Task.new do |t|
13
+ temp = t
14
+ end
15
+ temp.should == @task
16
+ end
17
+
18
+ it "should have blank rollout and rollback procs by default" do
19
+ @task.rollout.call.should be_nil
20
+ @task.rollback.call.should be_nil
21
+ end
22
+
23
+ it "should define rollout and rollback tasks by calling #rollback and #rollout with a block" do
24
+ @task.rollout { 1 + 2 }
25
+ @task.rollout.call.should == 3
26
+ @task.rollback { 4 + 2 }
27
+ @task.rollback.call.should == 6
28
+ end
29
+
30
+ describe "(with a rollout and rollback task defined)" do
31
+ before do
32
+ @task.rollout { 1 + 2 }
33
+ @task.rollback { 4 + 2 }
34
+ end
35
+
36
+ it "should run the rollback/rollout procs when called with #rollout!/#rollback!, returning the return value of the proc" do
37
+ @task.rollout!.should == 3
38
+ @task.rollback!.should == 6
39
+ end
40
+ end
41
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nickstenning-outback
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Stenning
@@ -32,13 +32,16 @@ extra_rdoc_files: []
32
32
 
33
33
  files:
34
34
  - lib/outback/manager.rb
35
- - lib/outback/shelltask.rb
36
- - lib/outback/yaml.rb
35
+ - lib/outback/task.rb
36
+ - lib/outback/task_helper.rb
37
37
  - lib/outback.rb
38
38
  - outback.gemspec
39
+ - Rakefile
40
+ - README.markdown
41
+ - spec/fixtures/example_manager.rb
39
42
  - spec/outback/manager_spec.rb
40
- - spec/outback/shelltask_spec.rb
41
- - spec/outback/yaml_spec.rb
43
+ - spec/outback/task_helper_spec.rb
44
+ - spec/outback/task_spec.rb
42
45
  - spec/spec_helper.rb
43
46
  has_rdoc: true
44
47
  homepage: http://github.com/nickstenning/outback
@@ -1,53 +0,0 @@
1
- require 'rubygems'
2
- require 'open4'
3
-
4
- module Outback
5
-
6
- class ShellTask
7
-
8
- attr_accessor :rollout, :rollback, :workdir, :name
9
- attr_reader :result, :errors, :exit_code
10
-
11
- def initialize(out, back)
12
- @rollout, @rollback = out, back
13
- @rolled_out = false
14
- end
15
-
16
- def rollout!
17
- run @rollout
18
- @rolled_out = true
19
- return @exit_code == 0
20
- end
21
-
22
- def rollback!
23
- run @rollback
24
- @rolled_out = false
25
- return @exit_code == 0
26
- end
27
-
28
- def rolled_out?
29
- @rolled_out
30
- end
31
-
32
- def reset_strings
33
- @result, @errors = "", ""
34
- end
35
-
36
- def run( command )
37
- reset_strings
38
- Dir.chdir(@workdir || Dir.getwd) do
39
- @status = Open4.popen4(*command) do |pid, i, o, e|
40
- @result = o.read
41
- @errors = e.read
42
- end
43
- @exit_code = @status.exitstatus
44
- end
45
- # Catch nonexistent commands at this point and return a sensible error
46
- rescue Errno::ENOENT => e
47
- @exit_code = 127
48
- @errors = e.message
49
- end
50
-
51
- end
52
-
53
- end
@@ -1,22 +0,0 @@
1
- module Outback
2
-
3
- module YAML
4
-
5
- def self.load( yaml_string )
6
- manager = Manager.new
7
- tasks = ::YAML.load(yaml_string)
8
- tasks.each do |task|
9
- task = [task['out'], task['back']]
10
- manager.tasks << Outback::ShellTask.new(*task)
11
- end
12
- return manager
13
- end
14
-
15
- def self.load_file( yaml_file )
16
- yaml = File.read( yaml_file )
17
- load( yaml )
18
- end
19
-
20
- end
21
-
22
- end
@@ -1,92 +0,0 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
2
-
3
- describe Outback::ShellTask do
4
-
5
- before do
6
- @task = Outback::ShellTask.new("out", "back")
7
- end
8
-
9
- it "should have an attr_accessor for @name" do
10
- @task.name.should be_nil
11
- @task.name = "Henry"
12
- @task.name.should == "Henry"
13
- end
14
-
15
- it "should use the first parameter to the rollout command" do
16
- @task.rollout.should == "out"
17
- end
18
-
19
- it "should use the second parameter as the rollback command" do
20
- @task.rollback.should == "back"
21
- end
22
-
23
- it "should allow its rollout command to be changed with #rollout=" do
24
- @task.rollout = "foo"
25
- @task.rollout.should == "foo"
26
- end
27
-
28
- it "should allow its rollback command to be changed with #rollback=" do
29
- @task.rollout = "bar"
30
- @task.rollout.should == "bar"
31
- end
32
-
33
- it "should not be rolled out by default" do
34
- @task.should_not be_rolled_out
35
- end
36
-
37
- it "should rollout when called with #rollout!" do
38
- @task.rollout!
39
- @task.should be_rolled_out
40
- end
41
-
42
- it "should run its commands in the current working directory if #workdir hasn't been set" do
43
- @task = Outback::ShellTask.new("pwd", "")
44
- @task.rollout!
45
- @task.result.chomp.should == Dir.getwd
46
- end
47
-
48
- it "should run its commands in the workdir, if set with #workdir=" do
49
- @task = Outback::ShellTask.new("pwd", "")
50
- @task.workdir = "/private/tmp" ## FIXME for *nix systems
51
- @task.rollout!
52
- @task.result.chomp.should == @task.workdir
53
- end
54
-
55
- it "should return true on successful #rollout! or #rollback!" do
56
- @task = Outback::ShellTask.new("echo", "echo")
57
- @task.rollout!.should be_true
58
- @task.rollback!.should be_true
59
- end
60
-
61
- it "should return false on unsuccessful #rollout! or #rollback!" do
62
- @task = Outback::ShellTask.new("I-am-a-command-most-unlikely-to-exist", "I-am-a-command-most-unlikely-to-exist")
63
- @task.rollout!.should be_false
64
- @task.rollback!.should be_false
65
- end
66
-
67
- describe "(when rolled out)" do
68
-
69
- before do
70
- @task = Outback::ShellTask.new('pwd', 'pwd')
71
- @task.rollout!
72
- end
73
-
74
- it "should be rolled out" do
75
- @task.should be_rolled_out
76
- end
77
-
78
- it "should have an integer exit code" do
79
- @task.exit_code.should be_a_kind_of(Fixnum)
80
- end
81
-
82
- it "should have a string result" do
83
- @task.result.should be_a_kind_of(String)
84
- end
85
-
86
- it "should have a string for errors (stderr)" do
87
- @task.errors.should be_a_kind_of(String)
88
- end
89
-
90
- end
91
-
92
- end
@@ -1,47 +0,0 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
2
- require 'yaml'
3
-
4
- describe Outback::YAML do
5
-
6
- before do
7
- @yaml =<<-EOM.gsub(/^\s{4}/, '')
8
- -
9
- out: touch y
10
- back: rm y
11
- -
12
- out: touch x
13
- back: rm x
14
- EOM
15
- @file = '/tmp/outback_yaml_spec_tmp'
16
- File.open(@file, 'w') { |f| f.puts @yaml }
17
- end
18
-
19
- after do
20
- File.delete(@file)
21
- end
22
-
23
- describe do
24
- it "should return an Outback::Manager object" do
25
- Outback::YAML.load(@yaml).should be_a_kind_of(Outback::Manager)
26
- Outback::YAML.load_file(@file).should be_a_kind_of(Outback::Manager)
27
- end
28
- it "should return an Outback::Manager object with as many tasks as the YAML passed into it" do
29
- Outback::YAML.load(@yaml).should have(2).tasks
30
- Outback::YAML.load_file(@file).should have(2).tasks
31
- end
32
- it "should add instances of Outback::ShellTask to the Outback::Manager's task array" do
33
- manager = Outback::YAML.load(@yaml)
34
- manager.tasks.first.should be_a_kind_of(Outback::ShellTask)
35
- end
36
- it "should set the rollout_command to the first element of the YAML array" do
37
- manager = Outback::YAML.load(@yaml)
38
- manager.tasks[0].rollout.should == "touch y"
39
- manager.tasks[1].rollout.should == "touch x"
40
- end
41
- it "should set the rollback_command to the second element of the YAML array" do
42
- manager = Outback::YAML.load(@yaml)
43
- manager.tasks[0].rollback.should == "rm y"
44
- manager.tasks[1].rollback.should == "rm x"
45
- end
46
- end
47
- end