How do I match all routes in Mojolicious? - perl

I'm trying to use a Mojolicious one-liner to display a message to users about a service outage. I'd like the message to display regardless of the route. Here's what I have which is slightly modified from what's in the documentation.
perl -Mojo -E 'a("/" => {text => "The service is down for maintenance."})->start' daemon
That works for / but not anything else. I added an asterisk to turn it into a wildcard route.
perl -Mojo -E 'a("/*" => {text => "The service is down for maintenance."})->start' daemon
That matches all routes except for /. Is there a way to match all routes in a single definition?

Yes, you can. Try these examples:
perl -Mojo -E 'app->routes->get( "/" => { text => "start" }); app->routes->get( "/*any" => { text => "any" }); app->start' get /
perl -Mojo -E 'app->routes->get( "/" => { text => "start" }); app->routes->get( "/*any" => { text => "any" }); app->start' get /some_route
Here you define catch all route *any after the specific one /
Documentation

How about:
perl -Mojo -E 'a("/*any" => {text => "The service is down for maintenance."})->start' daemon
I think it works for all urls but '/'.

If you create a named placeholder, with a default value of anything, I believe it does what you want:
perl -Mojo -E 'a("/*x" => { text => "The service is down for maintenance.", x => ''})->start' daemon
May not be the prettiest code you'll ever see, but it's only a few more characters :-)

Related

Puppet - restart a service after a each loop

I'm writing a Puppet module to enforce a configuration and restart the service if needed. I'd like to do something like that:
class app_cfg {
$app_files = ['file1', 'file2', 'file3']
$app_files.each |String $file| {
exec {"sed -i 's/bar/foo/g' $file":
path => ['/bin'],
onlyif => 'grep "bar" $file 2>/dev/null',
cwd => '/opt/app/config/',
}
} ~>
service { 'app':
ensure => running,
enable => true,
}
}
I could add "notify" inside the exec but I don't want to restart my service for each file, and it looks like the notify chaining arrow doesn't work after an each loop. Do you guys have any idea how to make it work?
Thanks a lot :)
I found a workaround by inserting a for loop inside the exec resource instead of using each:
class app_cfg {
service { 'app':
ensure => running,
enable => true,
}
exec {"for file in file1 file2 file3; do sed -i 's/bar/foo/g' \$file; done":
path => ['/bin'],
onlyif => 'grep "bar" $file 2>/dev/null',
cwd => '/opt/app/config/',
provider => shell,
notify => Service['app']
}
}
I'm still pretty curious about the first solution! Did you ever manage to do something like that?
Thanks!
I could add "notify" inside the exec but I don't want to restart my
service for each file, and it looks like the notify chaining arrow
doesn't work after an each loop.
It is important to understand that the code in a Puppet manifest describes only how to build a catalog that in turn describes the desired configuration of your server. The catalog lists the classes and resources declared, their parameters and properties, and the relationships among them. The logic within a manifest does not map to actions performed when applying the resulting catalog; it is reflected only in which classes and resources are catalogued, and what their parameters, properties, and relationships are.
Thus, when you use one of the chaining arrows in a manifest, you are instructing the catalog builder to record a relationship between the two resources in the catalog, not an explicit instruction to apply or refresh a resource. No declared resource will be applied at more than once or refreshed more times than it is applied. It will be perfectly fine, therefore, to put an appropriate chain expression inside your loop, but what you cannot do is put the Service declaration itself inside the loop, because that would produce multiple declarations of it. So you might do any of these:
Declare the service (once), and use a resource reference to it in your chain expression inside the loop:
class app_cfg {
service { 'app':
ensure => running,
enable => true,
}
['file1', 'file2', 'file3'].each |String $file| {
exec {"sed -i 's/bar/foo/g' $file":
path => ['/bin'],
onlyif => 'grep "bar" $file 2>/dev/null',
cwd => '/opt/app/config/',
} ~> Service['App']
}
}
As above, but use the appropriate resource metaparameter instead of a chain expression:
class app_cfg {
service { 'app':
ensure => running,
enable => true,
}
['file1', 'file2', 'file3'].each |String $file| {
exec {"sed -i 's/bar/foo/g' $file":
path => ['/bin'],
onlyif => 'grep "bar" $file 2>/dev/null',
cwd => '/opt/app/config/',
notify => Service['app'],
}
}
}
Or declare the relationships with the help of a collector:
class app_cfg {
['file1', 'file2', 'file3'].each |String $file| {
exec {"sed -i 's/bar/foo/g' $file":
path => ['/bin'],
onlyif => 'grep "bar" $file 2>/dev/null',
cwd => '/opt/app/config/',
tag => 'app_notifier',
}
}
Exec<|tag == 'app_notifier|>
~> service { 'app':
ensure => running,
enable => true,
}
}

How do I ensure a Puppet exec resource always includes `set -o pipefail`?

In a particular project, I have a lot of Puppet exec resources with pipes. This seems to work just fine.
exec { 'foobar':
command => 'foo | bar',
}
However, there are occasions where foo fails. The default behavior is to report the exit code only for the last command in the pipeline. I can fix this manually.
exec { 'foobar':
command => 'set -o pipefail; foo | bar',
provider => shell,
}
But, I want to make sure this happens in all these cases automatically. I'd like to avoid manual find/replace and auditing all uses of exec.
Am I missing some useful attribute?
Is there a wrapper I can use?
Am I, unfortunately, looking at a custom resource?
Am I missing some useful attribute?
No, Exec has no attribute that would automatically prepend additional code to the command.
Is there a wrapper I can use?
I'm not confident I understand what you mean by a "wrapper", but do see below.
Am I, unfortunately, looking at a custom resource?
If you're asking whether you need to implement a native custom type, then surely not. You can undoubtedly address this problem with a (DSL-level) defined type, though you will need to adjust all your Exec declarations to be declarations of the defined type instead. This may be what you mean by a "wrapper" -- I'm fairly sure there's no existing one for your particular purpose, but it shouldn't be hard to create one:
define mymodule::exec (
command => $title,
creates => 'NOT SET',
cwd => 'NOT SET',
# ... all other regular parameters ...
) {
$real_provider = $provider ? { 'NOT SET' => 'shell', default => $provider }
if $real_provider == 'shell' {
$real_command = "set -o pipefail; $command"
} else {
warning('Non-shell command declared via mymodule::exec')
$real_command = $command
}
exec { $title:
command => $real_command,
provider => $real_provider,
creates => $creates ? { 'NOT SET' => undef, default => $creates },
cwd => $cwd ? { 'NOT SET' => undef, default => $cwd },
# ... all remaining regular parameters ...
}
}

Perl Message::Stack example

I am using Perl to try to implement an example for the Message::Stack module from CPAN.
I get the error
Can't locate object method "new" via package "Message::Stack" at stack.pl line 3.
my $stack = Message::Stack->new;
$stack->add(Message::Stack::Message->new(
msgid => 'something_happened',
level => 'error',
scope => 'login_formm',
subject => 'username',
text => 'Something happened!'
));
# Or... for those that want to type less
$stack->add({
msgid => 'something_else_happened',
level => 'error',
scope => 'login_form',
subject => 'password',
text => 'Something else happened!'
});
# ...
my $errors = $stack->for_level('error');
# Or
my $login_form_errors = $stack->for_scope('login_form');
$login_form_errors->for_subject('username');
print "Username has ".$login_form_errors->count." errors.\n";
Please let me know which object I need to load.
I am sure that what the message actually says is
Can't locate object method "new" via package "Message::Stack" (perhaps you forgot to load "Message::Stack"?) at stack.pl line 5.
So, did you forget?
And later, when you actually try to load it using
use Message::Stack
and it tells you
Can't locate Message/Stack.pm in #INC
doesn't it occur to you that it's not found because it isn't there, and needs installing?

What is wrong with my declaration of a hash inside a hash in Perl?

I am struggling with the following declaration of a hash in Perl:
my %xmlStructure = {
hostname => $dbHost,
username => $dbUsername,
password => $dbPassword,
dev_table => $dbTable,
octopus => {
alert_dir => $alert_dir,
broadcast_id => $broadcast_id,
system_id => $system_id,
subkey => $subkey
}
};
I've been googling, but I haven't been able to come up with a solution, and every modification I make ends up in another warning or in results that I do not want.
Perl complaints with the following text:
Reference found where even-sized list expected at ./configurator.pl line X.
I am doing it that way, since I want to use the module:
XML::Simple
In order to generate a XML file with the following structure:
<settings>
<username></username>
<password></password>
<database></database>
<hostname></hostname>
<dev_table></dev_table>
<octopus>
<alert_dir></alert_dir>
<broadcast_id></broadcast_id>
<subkey></subkey>
</octopus>
</settings>
so sometthing like:
my $data = $xmlFile->XMLout(%xmlStructure);
warn Dumper($data);
would display the latter xml sample structure.
Update:
I forgot to mention that I also tried using parenthesis instead of curly braces for the hash reference, and eventhough it seems to work, the XML file is not written properly:
I end up with the following structure:
<settings>
<dev_table>5L3IQWmNOw==</dev_table>
<hostname>gQMgO3/hvMjc</hostname>
<octopus>
<alert_dir>l</alert_dir>
<broadcast_id>l</broadcast_id>
<subkey>l</subkey>
<system_id>l</system_id>
</octopus>
<password>dZJomteHXg==</password>
<username>sjfPIQ==</username>
</settings>
Which is not exactly wrong, but I'm not sure if I'm going to have problems latter on as the XML file grows bigger. The credentials are encrypted using RC4 algorith, but I am encoding in base 64 to avoid any misbehavior with special characters.
Thanks
{} are used for hash references. To declare a hash use normal parentheses ():
my %xmlStructure = (
hostname => $dbHost,
username => $dbUsername,
password => $dbPassword,
dev_table => $dbTable,
octopus => {
alert_dir => $alert_dir,
broadcast_id => $broadcast_id,
system_id => $system_id,
subkey => $subkey
}
);
See also perldoc perldsc - Perl Data Structures Cookbook.
For your second issue, you should keep in mind that XML::Simple is indeed too simple for most applications. If you need a specific layout, you're better off with a different way of producing the XML, say, using HTML::Template. For example (I quoted variable names for illustrative purposes):
#!/usr/bin/env perl
use strict; use warnings;
use HTML::Template;
my $tmpl = HTML::Template->new(filehandle => \*DATA);
$tmpl->param(
hostname => '$dbHost',
username => '$dbUsername',
password => '$dbPassword',
dev_table => '$dbTable',
octopus => [
{
alert_dir => '$alert_dir',
broadcast_id => '$broadcast_id',
system_id => '$system_id',
subkey => '$subkey',
}
]
);
print $tmpl->output;
__DATA__
<settings>
<username><TMPL_VAR username></username>
<password><TMPL_VAR password></password>
<database><TMPL_VAR database></database>
<hostname><TMPL_VAR hostname></hostname>
<dev_table><TMPL_VAR dev_table></dev_table>
<octopus><TMPL_LOOP octopus>
<alert_dir><TMPL_VAR alert_dir></alert_dir>
<broadcast_id><TMPL_VAR broadcast_id></broadcast_id>
<subkey><TMPL_VAR subkey></subkey>
<system_id><TMPL_VAR system_id></system_id>
</TMPL_LOOP></octopus>
</settings>
Output:
<settings>
<username>$dbUsername</username>
<password>$dbPassword</password>
<database></database>
<hostname>$dbHost</hostname>
<dev_table>$dbTable</dev_table>
<octopus>
<alert_dir>$alert_dir</alert_dir>
<broadcast_id>$broadcast_id</broadcast_id>
<subkey>$subkey</subkey>
<system_id>$system_id</system_id>
</octopus>
</settings>
You're using the curly braces { ... } to construct a reference to an anonymous hash. You should either assign that to a scalar, or change the { ... } to standard parentheses ( ... ).

"Can't call method "dir_path" on an undefined value" when running Mason component on the command line

Greetings,
I'm trying to develop some tests for Mason components which requires running them on the command line instead of the web server. When I try this, I get an error:
perl -MHTML::Mason::Request -MHTML::Mason::Interp -I./lib \
-e '$int = HTML::Mason::Interp->new( data_dir => "/home/friedo/cache", comp_root => "/home/friedo/comps" ); $m = HTML::Mason::Request->new( comp => "/dummy", interp => $int ); $m->comp("/dummy")'
Results in:
Can't call method "dir_path" on an undefined value at lib/HTML/Mason/Request.pm line 1123.
The error is thrown when the call to ->comp is attempted. I can't figure out what's wrong with the configuration. The component is there and appears to be compiled just fine, and it works via Apache.
This is using HTML::Mason 1.35.
Edit: Let's try a bounty for this one. The alternative is me having to dive deep into Mason's guts! :)
Edit again: Thanks very much to David for pointing out the crucial detail that I missed for getting this to work.
This was actually for a test framework that needed to exercise a module that calls some Mason comps -- under normal operation the module is provided with a Mason request object to use for that purpose, but I couldn't get that to work offline. The key was using an Interpreter object instead, so I ended up doing the following, which is a little silly but makes the tests work:
sub _mason_out {
...
my $buf;
if ( $ENV{MASON_TEST} ) {
my $int = HTML::Mason::Interp->new( comp_root => $self->{env}->comp_dir,
out_method => \$buf );
$int->exec( $comp, %args );
} else {
my $m = $self->{mason_object};
$m->comp( { store => \$buf }, $comp, %args );
}
return $buf;
}
I think this fails because your Request object hasn't built a component stack at the point that it is called. Use the Interp->exec() method instead as described in Using Mason from a Standalone Script
perl -MHTML::Mason::Interp -I./lib \
-e 'HTML::Mason::Interp->new( data_dir => "/home/friedo/cache", comp_root => "/home/friedo/comps" )->exec("/dummy")'