File Coverage

blib/lib/Beam/Minion/Util.pm
Criterion Covered Total %
statement 82 91 90.1
branch 15 18 83.3
condition n/a
subroutine 15 16 93.7
pod 3 3 100.0
total 115 128 89.8


line stmt bran cond sub pod time code
1             package Beam::Minion::Util;
2             our $VERSION = '0.019';
3             # ABSTRACT: Utility functions for Beam::Minion
4              
5             #pod =head1 SYNOPSIS
6             #pod
7             #pod use Beam::Minion::Util qw( minion );
8             #pod
9             #pod my $minion = minion();
10             #pod my %attrs = minion_attrs();
11             #pod
12             #pod =head1 DESCRIPTION
13             #pod
14             #pod This module contains helper routines for L.
15             #pod
16             #pod =head1 SEE ALSO
17             #pod
18             #pod L
19             #pod
20             #pod =cut
21              
22 9     9   444345 use Mojo::Base -strict, -signatures;
  9         14726  
  9         53  
23 9     9   18300 use Exporter qw( import );
  9         12  
  9         288  
24 9     9   4483 use Minion;
  9         3028255  
  9         74  
25 9     9   4770 use Beam::Runner::Util qw( find_containers );
  9         120898  
  9         710  
26 9     9   81 use Scalar::Util qw( weaken );
  9         18  
  9         430  
27 9     9   5071 use Mojolicious;
  9         1527505  
  9         115  
28 9     9   389 use Mojo::Log;
  9         17  
  9         63  
29 9     9   6392 use Beam::Wire;
  9         3190608  
  9         482  
30 9     9   5520 use YAML::PP qw( LoadFile );
  9         543647  
  9         754  
31 9     9   4386 use Log::Any qw($LOG);
  9         75067  
  9         61  
32              
33             our @EXPORT_OK = qw( minion_init_args minion build_mojo_app );
34              
35             our %BACKEND = (
36             sqlite => 'SQLite',
37             postgres => 'Pg',
38             mongodb => 'MongoDB',
39             mysql => 'mysql',
40             );
41              
42             # The saved init args, in case we override them with Beam::Minion->init
43             our @INIT_ARGS = ();
44              
45             #pod =sub minion_init_args
46             #pod
47             #pod my %args = minion_init_args();
48             #pod
49             #pod Get the arguments needed to initialize a new Minion instance by parsing
50             #pod the C environment variable.
51             #pod
52             #pod This environment variable can take a few forms:
53             #pod
54             #pod =over
55             #pod
56             #pod =item file://
57             #pod
58             #pod A path to a YAML file of configuration to read. Should contain a single key and value
59             #pod which are the Minion backend to use and the argument to its constructor.
60             #pod
61             #pod =item
62             #pod
63             #pod A simple backend URL like C,
64             #pod C, C, or
65             #pod C. The following backends are supported:
66             #pod L, L,
67             #pod L, L.
68             #pod
69             #pod =item +
70             #pod
71             #pod A backend name and arguments, separated by C<+>, like
72             #pod C. Any backend may be used this way.
73             #pod
74             #pod If your backend requires more arguments, you can separate them with
75             #pod C<+>:
76             #pod
77             #pod # Configure the MySQL backend with a DBI DSN
78             #pod BEAM_MINION=mysql+dsn+dbi:mysql:minion
79             #pod
80             #pod =back
81             #pod
82             #pod =cut
83              
84             sub minion_init_args {
85 16 100   16 1 231289 if (@_) {
86 1         2 @INIT_ARGS = @_;
87             }
88 16 100       40 if (@INIT_ARGS) {
89 4         39 return @INIT_ARGS;
90             }
91             die "You must set the BEAM_MINION environment variable to the Minion database URL.\n"
92             . "See `perldoc Beam::Minion` for getting started instructions.\n"
93 12 100       150 unless $ENV{BEAM_MINION};
94 9         13 my ( $backend, $url );
95             # If there's a `+`, we must be doing multi-args
96 9 100       32 if ( $ENV{BEAM_MINION} =~ /^[^+:]+\+/ ) {
97 1         4 @INIT_ARGS = split /\+/, $ENV{BEAM_MINION};
98 1         5 return @INIT_ARGS;
99             }
100 8         39 my ( $schema ) = $ENV{BEAM_MINION} =~ /^([^:]+)/;
101             # Load config from a YAML file
102 8 100       29 if ($schema eq 'file') {
103 1         13 my ( undef, $path ) = split '://', $ENV{BEAM_MINION};
104 1         6 @INIT_ARGS = (LoadFile($path))[0]->%*;
105 1         6405 return @INIT_ARGS;
106             }
107 7         28 @INIT_ARGS = ($BACKEND{ $schema }, $ENV{BEAM_MINION});
108 7         45 return @INIT_ARGS;
109             }
110              
111             #pod =sub minion
112             #pod
113             #pod my $minion = minion();
114             #pod
115             #pod Get a L instance as configured by the C environment
116             #pod variable (parsed by L).
117             #pod
118             #pod =cut
119              
120             sub minion {
121 8     8 1 41 return Minion->new( minion_init_args );
122             }
123              
124             #pod =sub build_mojo_app
125             #pod
126             #pod Build the L app that contains the L plugin
127             #pod (L) and tasks. This can then be given to
128             #pod one of the L classes to execute commands.
129             #pod
130             #pod =cut
131              
132             sub build_mojo_app {
133 5     5 1 62 my $app = Mojolicious->new;
134             # Remove Mojo::Log from STDERR so that we don't double-log
135 5         76516 $app->log(Mojo::Log->new(handle => undef));
136             # Forward Mojo::Log logs to the Log::Any logger, so that from there
137             # they will be forwarded to OpenTelemetry.
138             # Modules should prefer to log with Log::Any because it supports
139             # structured logging.
140 0     0     $app->log->on( message => sub ( $, $level, @lines ) {
  0            
  0            
  0            
141 0         0 $LOG->$level(@lines);
142 5         170 });
143              
144 5         62 push @{$app->commands->namespaces}, 'Minion::Command';
  5         22  
145              
146 5         395 my $minion = minion();
147 3     2   173046 $app->helper(minion => sub {$minion});
  2         2350  
148              
149 3         375 my %container = find_containers();
150 3         2529 for my $container_name ( keys %container ) {
151 6         12 my $path = $container{ $container_name };
152 6         133 my $wire = Beam::Wire->new( file => $path );
153 6         46923 my $config = $wire->config;
154 6         36 for my $service_name ( keys %$config ) {
155 15 100       145 next unless $wire->is_meta( $config->{ $service_name }, 1 );
156 12         599 my $task_name = "$container_name:$service_name";
157             $minion->add_task( $task_name => sub {
158 1     1   9701 my ( $job, @args ) = @_;
159 1         64 local $LOG->context->{task} = $task_name;
160 1         119 local $LOG->context->{job} = $job->id;
161 1         26 $LOG->info('Running task');
162 1         67 my $wire = Beam::Wire->new( file => $path );
163              
164 1         8505 local $@;
165 1         3 my $obj = eval { $wire->get( $service_name, lifecycle => 'factory' ) };
  1         7  
166 1 50       17624 if ( my $error = $@ ) {
167 0         0 $LOG->error('Error getting runnable service', { service => $service_name, error => $error });
168 0         0 return $job->fail( { error => $error } );
169             }
170              
171 1         70 local $@;
172 1         5 my $exit = eval { $obj->run( @args ) };
  1         11  
173 1 50       17 if ( my $error = $@ ) {
174 0         0 $LOG->error('Error running task', { error => $error });
175 0         0 return $job->fail( { error => $error } );
176             }
177              
178 1 50       11 my $method = $exit ? 'fail' : 'finish';
179 1         27 $LOG->info('Task result', { exit => $exit, state => $method });
180 1         24 $job->$method( { exit => $exit } );
181 12         68 } );
182             }
183 6         76 undef $wire;
184             }
185              
186 3         85 return $app;
187             }
188              
189             1;
190              
191             __END__