App-mqtt2job

 view release on metacpan or  search on metacpan

bin/mqtt2job  view on Meta::CPAN

#!/usr/bin/env perl
use strict;
use warnings;

use Net::MQTT::Simple;
use JSON;
use DateTime;
use Try::Tiny;
use File::Temp;
use Capture::Tiny ':all';

use App::mqtt2job qw/ helper_v1 ha_helper_cfg /;

package MQTT::Job::Options;

use Moose;
with 'MooseX::Getopt';

# required options
has 'mqtt_server'   => (is => "rw", isa => "Str", required => 1 );
has 'base_topic'    => (is => "rw", isa => "Str", required => 1 );
has 'job_dir'       => (is => "rw", isa => "Str", required => 1 );

# optional
has 'mqtt_port'     => (is => "rw", isa => "Int", default => 1883  );
has 'shebang'       => (is => "rw", isa => "Str", default => $^X );
has 'suffix'        => (is => "rw", isa => "Str", default => "pl" );
has 'process_name'  => (is => "rw", isa => "Str", default => "mqtt2job" );
has 'helper_script' => (is => "rw", isa => "Str", default => "helper_v1" );
has 'no_unlink'     => (is => "rw", isa => "Bool" );

# helper modes
has 'ha_helper'     => (is => "rw", isa => "Bool" );
has 'task'          => (is => "rw", isa => "Str", default => "unknown_task" );
has 'cmd'           => (is => "rw", isa => "Str", default => "unknown_cmd" );
has 'args'          => (is => "rw", isa => "Str", default => "" );

# TODO 
#has 'max_velocity'  => (is => "rw", isa => "Int", default => 59  ); # limit triggers to ~once per minute
#has 'timeout'       => (is => "rw", isa => "Int", default => 30  ); # Terminate a job if it runs longer than x seconds
#has 'allowed_tpl'   => (is => "rw", isa => "Str" ); # command template (e.g. regex/string)
#has 'allowed_file'  => (is => "rw", isa => "Str" ); # file containing allowed cmds, timeouts, and max velocities
#has 'cert'          => (is => "rw", isa => "Str" ); # provide cert for request validity check
#has 'cron_pattern'  => (is => "rw", isa => "Bool" ); # use cron pattern to generate ha_helper template

package main;

# PODNAME: mqtt2job
# ABSTRACT: Subscribe to an MQTT topic and trigger job execution

my $opt = MQTT::Job::Options->new_with_options;
$0 = $opt->process_name;

my $mqtt = Net::MQTT::Simple->new($opt->mqtt_server . ":" . $opt->mqtt_port);

my $on_exit = sub {
    my $no_warn = shift;
    printf STDERR "\nProcess %s terminating, disconnecting from %s:%s\n", $0, $opt->mqtt_server, $opt->mqtt_port unless $no_warn;
    $mqtt->disconnect;
    exit;
};

# clean up on exit
$SIG{INT} = $on_exit;
$SIG{USR1} = $on_exit;

if ($opt->ha_helper) {
    warn( sprintf("Did not find %s in %s directory\n", $opt->cmd, $opt->job_dir) ) unless _cmd_ok($opt->cmd);
    print ha_helper_cfg({ task => $opt->task, cmd => $opt->cmd, args => $opt->args, base_topic => $opt->base_topic });
    $on_exit->(1);
}

$mqtt->subscribe($opt->base_topic, \&_on_message);
$mqtt->run;

sub _on_message {
    my ($topic, $message) = @_;
    my $dt = DateTime->now();
    my $obj = undef;

    try {
        $obj = decode_json($message);
    };

    my $msg = "";

    # check command and that it exists where expected
    if ($obj->{cmd} && _cmd_ok($obj->{cmd})) {
        
        # copy options into obj array
        foreach my $from_opt (qw/ mqtt_server mqtt_port base_topic job_dir shebang suffix no_unlink helper_script/) {
	        $obj->{$from_opt} = $opt->$from_opt;
		}

        my $helper_script = undef;

        if (my $cr = __PACKAGE__->can($opt->helper_script)) {
            $helper_script = $cr->($obj);
        } else {
            warn("Cannot use helper script of [" . $opt->helper_script . "], falling back\n");
            $helper_script = helper_v1($obj);
        }
        
        # Yee-ha!
        system(join(" ", $opt->shebang, $helper_script, "&"));

        $msg .=  "- running $helper_script";
    } else {
        $msg = "Unknown command requested";
    }

    print STDERR "$msg: $topic - $message\n";
}

sub _cmd_ok {
    my $cmd = shift;
    # example command checker
    $cmd =~ s/[^\w\-\.]//g;
    return (-f $opt->job_dir . "/" . $cmd) ? 1 : undef;
}

__END__

=pod

=encoding UTF-8

=head1 NAME

mqtt2job - Subscribe to an MQTT topic and trigger job execution

=head1 VERSION

version 0.03

=head1 SYNOPSIS

  mqtt2job --mqtt_server mqtt.example.com --base_topic my/topic --job_dir /apps



( run in 0.852 second using v1.01-cache-2.11-cpan-354807fb38d )