#!/usr/bin/perl
#
# dashboard-facter - Maintenance of external facts for use in tools dashboard
#
# Written by Jon Robertson <jonrober@stanford.edu>
# Copyright 2014
#     The Board of Trustees of the Leland Stanford Junior University

#############################################################################
# Modules and declarations
#############################################################################

use 5.010;
use autodie;
use strict;
use warnings;

use Getopt::Long::Descriptive;
use IO::Handle;
use JSON;
use Net::Remctl::Backend;
use Perl6::Slurp;
use Stanford::Infrared::General qw(load_json_file save_json_file);
use Stanford::Infrared::Wrappers qw(print_stdout);

# Our option descriptions, for both defining options and their usage.
our @OPTIONS = (
    ['help|h',     'print usage (this text) and exit'],
    ['manual|man', 'print perldoc and exit'],
);

# File for our custom facts.
our $FACTER_FNAME = '/etc/facter/facts.d/dashboard.json';

#############################################################################
# Command routines
#############################################################################

# Add a fact into our external fact list.  If the fact already exists, tell
# the user the old value before overwriting.
#
# $fact  - Name of the external fact
# $value - Value to set that fact to
#
# Returns: Nothing
sub cmd_add {
    my ($fact, $value) = @_;

    my $facts = load_json_file($FACTER_FNAME);
    if (exists $facts->{$fact}) {
        warn "Updating fact, previously set to: " . $facts->{$fact} . "\n";
    }
    $facts->{$fact} = $value;
    save_json_file($facts, $FACTER_FNAME);

    return;
}

# Delete a fact from our external fact list.  If the fact does not exist,
# tell the user.
#
# $fact - Name of the external fact
#
# Returns: Nothing
#          Dies with a string if the fact does not exist
sub cmd_delete {
    my ($fact) = @_;
    my $facts = load_json_file($FACTER_FNAME);
    if (exists $facts->{$fact}) {
        delete $facts->{$fact};
        save_json_file($facts, $FACTER_FNAME);
    } else {
        die "external fact $fact does not exist in our file!\n";
    }

    return;
}

# Find out whether or not a fact exists and return as an exit code.
#
# $fact - Name of the external fact
#
# Returns: 0 if the external fact exists (to be the exit value for prog)
#          255 if it does not
sub cmd_exists {
    my ($fact) = @_;
    my $facts = load_json_file($FACTER_FNAME);
    if (exists $facts->{$fact}) {
        return 0;
    } else {
        return 255;
    }
}

# Print out the current list of external facts.  This is printed in the
# JSON format they are stored in for general readability.
#
# Returns: Nothing
sub cmd_list {

    if (-e $FACTER_FNAME) {
        my $json = slurp($FACTER_FNAME);
        print_stdout($json);
    } else {
        print_stdout("No external facts have been added to this system.\n");
    }

    return;
}

#############################################################################
# Main routine
#############################################################################

# Get errors and output in the same order.
STDOUT->autoflush;

# Clean up the path name.
my $fullpath = $0;
$0 =~ s{ ^ .* / }{}xms;

# Parse command-line options.
my ($options, $usage) = describe_options("$0 %o <args>", @OPTIONS);
if ($options->manual) {
    print_stdout("Feeding myself to perldoc, please wait....\n");
    exec 'perldoc', '-t', $fullpath;
} elsif ($options->help) {
    print_stdout($usage->text);
    exit 0;
}

my %commands = (
                'add' => {
                    code     => \&cmd_add,
                    args_min => 2,
                    args_max => 2,
                    syntax   => '<fact> <value>',
                    summary  => 'Add or edit a fact',
                },
                'delete' => {
                    code     => \&cmd_delete,
                    args_min => 1,
                    args_max => 1,
                    syntax   => '<fact>',
                    summary  => 'Delete a fact',
                },
                'list' => {
                    code     => \&cmd_list,
                    args_min => 0,
                    args_max => 0,
                    syntax   => '',
                    summary  => 'List all external facts',
                },
                'exists' => {
                    code     => \&cmd_exists,
                    args_min => 1,
                    args_max => 1,
                    syntax   => '<fact>',
                    summary  => 'Check to see if a fact exists',
                },
               );
my $backend = Net::Remctl::Backend->new(
    {
        commands    => \%commands,
        command     => 'facter-external',
        help_banner => 'facter-external admin remctl help:',
    }
);
my $run = $backend->run;
$run ||= 0;
exit $run;

__END__

##############################################################################
# Documentation
##############################################################################

=head1 NAME

dashboard-facter - Maintenance of external facts for use in tools dashboard

=head1 SYNOPSIS

B<dashboard-facter> [B<-h>] [B<--manual>]

=head1 DESCRIPTION

This script is meant to maintain a JSON file containing a list of
simple variables and their values.  The file is then read by facter as
a list of external facts.  The intended result is to push a number of
per-system facts out that can then be used in the dashboard to display
system status for various lists of "has X been done to a system?".

=head1 OPTIONS

=over 4

=item B<-h>, B<--help>

Prints a short command summary for the script.

=item B<--manual>, B<--man>

Prints the perldoc information (this document) for the script.

=item B<add> <fact> <value>

Adds a new fact to the list, or overwrites an existing fact.  In the
latter case we also warn the user of the old value in case they want to
back out.

=item B<delete> <fact>

Deletes an existing fact.

=item B<list>

Prints out the JSON listing off all current external facts and their
values.

=back

=head1 AUTHORS

Jon Robertson <jonrober@stanford.edu>

=cut
