nickstenning-outback 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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