File Coverage

lib/Rex/Transaction.pm
Criterion Covered Total %
statement 34 42 80.9
branch 1 2 50.0
condition n/a
subroutine 8 9 88.8
pod 2 2 100.0
total 45 55 81.8


line stmt bran cond sub pod time code
1             #
2             # (c) Jan Gehring
3             #
4              
5             =head1 NAME
6              
7             Rex::Transaction - Transaction support
8              
9             =head1 DESCRIPTION
10              
11             With this module you can define transactions and rollback scenarios on failure.
12              
13             =head1 SYNOPSIS
14              
15             use Rex::Transaction;
16              
17             task 'do-something', 'server01', sub {
18             transaction {
19             on_rollback {
20             rmdir '/tmp/mydata';
21             };
22              
23             mkdir '/tmp/mydata';
24             upload 'files/myapp.tar.gz', '/tmp/mydata';
25             run 'tar xzf myapp.tar.gz -C /tmp/mydata';
26             if ( $? != 0 ) { die('Error extracting myapp.tar.gz'); }
27             };
28             };
29              
30             =head1 EXPORTED FUNCTIONS
31              
32             =cut
33              
34             package Rex::Transaction;
35              
36 22     22   327 use v5.12.5;
  22         79  
37 22     22   123 use warnings;
  22         47  
  22         1058  
38              
39             our $VERSION = '1.14.3'; # VERSION
40              
41             require Exporter;
42              
43 22     22   125 use vars qw(@EXPORT @ROLLBACKS);
  22         45  
  22         1052  
44 22     22   288 use base qw(Exporter);
  22         74  
  22         1766  
45              
46 22     22   169 use Rex::Logger;
  22         98  
  22         162  
47 22     22   1067 use Rex::TaskList;
  22         37  
  22         188  
48 22     22   571 use Data::Dumper;
  22         129  
  22         7921  
49              
50             @EXPORT = qw(transaction on_rollback);
51              
52             =head2 transaction($codeRef)
53              
54             Start a transaction for C<$codeRef>. If C<$codeRef> dies, Rex will run the L code to roll back the transaction.
55              
56             task 'deploy', group => 'frontend', sub {
57             on_rollback {
58             rmdir '...';
59             };
60              
61             deploy 'myapp.tar.gz';
62             };
63              
64             task 'restart_server', group => 'frontend', sub {
65             service apache2 => 'restart';
66             };
67              
68             task 'all', group => 'frontend', sub {
69             transaction {
70             do_task [qw/deploy restart_server/];
71             };
72             };
73              
74             =cut
75              
76             sub transaction(&) {
77 2     2 1 76 my ($code) = @_;
78 2         6 my $ret = 1;
79              
80 2         7 Rex::Logger::debug("Cleaning ROLLBACKS array");
81 2         20 @ROLLBACKS = ();
82              
83 2         82 Rex::TaskList->create()->set_in_transaction(1);
84              
85 2         6 eval { &$code(); };
  2         6  
86              
87 2 50       18 if ($@) {
88 2         23 my $err = $@;
89 2         34 Rex::Logger::info("Transaction failed. Rolling back.");
90              
91 2         27 $ret = 0;
92 2         52 for my $rollback_code ( reverse @ROLLBACKS ) {
93              
94             # push the connection of the task back
95 0         0 Rex::push_connection( $rollback_code->{"connection"} );
96              
97             # run the rollback code
98 0         0 &{ $rollback_code->{"code"} }($err);
  0         0  
99              
100             # and pop it away
101 0         0 Rex::pop_connection();
102             }
103              
104 2         54 Rex::TaskList->create()->set_in_transaction(0);
105              
106 2         50 die("Transaction failed. Rollback done.");
107             }
108              
109 0           Rex::TaskList->create()->set_in_transaction(0);
110              
111 0           return $ret;
112              
113             }
114              
115             =head2 on_rollback($codeRef)
116              
117             This will execute C<$codeRef> if a step in the L fails.
118              
119             =cut
120              
121             sub on_rollback(&) {
122 0     0 1   my ($code) = @_;
123              
124 0           push(
125             @ROLLBACKS,
126             {
127             code => $code,
128             connection => Rex::get_current_connection()
129             }
130             );
131             }
132              
133             1;