CallBackery

 view release on metacpan or  search on metacpan

lib/CallBackery/Config.pm  view on Meta::CPAN

# $Id: Config.pm 539 2013-12-09 22:28:11Z oetiker $
package CallBackery::Config;

=head1 NAME

CallBackery::Config - get parse configuration file for CallBackery

=head1 SYNOPSIS

 use Nq::Config;
 my $cfg = CallBackery::Config->new(file=>$file);
 my $hash_ref = $cfg->cfgHash();
 my $pod = $cfg->pod();

=head1 DESCRIPTION

CallBackery gets much of its configuration from this config file.

=cut

use Mojo::Base -base,-async_await, -signatures;
use CallBackery::Exception qw(mkerror);
use CallBackery::Translate qw(trm);
use Config::Grammar::Dynamic;
use Carp;
use autodie;
use File::Spec;
use Locale::PO;
use Mojo::Promise;
use Mojo::Loader qw(load_class);
use Mojo::JSON qw(true false);
use Mojo::Exception;
use Scalar::Util qw(blessed);
# use Devel::Cycle;

=head2 file

the name of the config file

=cut

has file => sub { croak "the file parameter is mandatory" };

has secretFile => sub ($self) {
    my $secretFile = $self->file.'.secret';
    if (not -f $secretFile){
        open my $rand, '>', $secretFile;
        chmod 0600,$secretFile;
        print $rand sprintf('%x%x',int(rand()*1e14),int(rand()*1e14));
        close $rand;
        chmod 0400,$secretFile;
    }
    return $secretFile;
};

has app => sub { croak "the app parameter is mandatory" }, weak => 1;

has log => sub {
    shift->app->log;
};

=head2 cfgHash

a hash containing the data from the config file

=cut

has cfgHash => sub {
    my $self = shift;
    my $cfg_file = shift;
    my $parser = $self->makeParser();
    my $cfg = $parser->parse($self->file, {encoding => 'utf8'}) or croak($parser->{err});
    # the grammar is self referential, so we need to clean it up
    $self->grammar(undef);
    return $cfg;
};

=head2 pod

returns a pod documenting the config file

=cut

has pod => sub {
    my $self = shift;
    my $parser = $self->makeParser();
    my $E = '=';
    my $footer = <<"FOOTER";

${E}head1 COPYRIGHT

Copyright (c) 2014 by OETIKER+PARTNER AG. All rights reserved.

${E}head1 AUTHOR

S<Tobias Oetiker E<lt>tobi\@oetiker.chE<gt>>
S<Fritz Zaucker E<lt>fritz.zaucker\@oetiker.chE<gt>>

${E}head1 HISTORY

 2014-01-11 to 1.0 first version
 2014-04-29 fz 1.1 implement plugin path

FOOTER
    my $header = <<"HEADER";
${E}head1 NAME

callbackery.cfg - The Appliance FRONTEND Builder config file

${E}head1 SYNOPSIS

lib/CallBackery/Config.pm  view on Meta::CPAN

    open my $fh ,'<', \$config;
    my $zip = Archive::Zip->new();
    $zip->readFromFileHandle($fh);
    my %stateFileCache;
    for my $member ($zip->members){
        for ($member->fileName){
            /^\{DATABASE\}$/ && do {
                $self->log->warn("Restoring Database!");
                $self->app->database->mojoSqlDb->disconnect;
                unlink glob $cfg->{BACKEND}{cfg_db}.'*';
                $member->extractToFileNamed($cfg->{BACKEND}{cfg_db});
                last;
            };
            /^\{DATABASEDUMP\}$/ && do {
                $self->log->warn("Restoring Database Dump!");
                $self->app->database->mojoSqlDb->disconnect;
                unlink glob $cfg->{BACKEND}{cfg_db}.'*';
                open my $sqlite, '|-', '/usr/bin/sqlite3',$cfg->{BACKEND}{cfg_db};
                my $sql = $member->contents();
                $sql =~ s/0$//; # for some reason the dump ends in 0
                print $sqlite $sql;
                close $sqlite;
                last;
            };
            m/^\{PLUGINSTATE\.([^.]+)\}(.+)/ && do {
                my $plugin = $1;
                my $file = $2;
                if (not $stateFileCache{$plugin}){
                    my $obj = eval {
                         $self->instantiatePlugin($plugin,$user);
                    };
                    if (not $obj){
                        $self->log->warn("Ignoring $file from plugin $plugin since the plugin is not available here.");
                        next;
                    }
                    $stateFileCache{$plugin} = { map { $_ => 1 } @{$obj->stateFiles} };
                };
                if ($stateFileCache{$plugin}{$file}){
                    $member->extractToFileNamed($file);
                }
                else {
                    $self->log->warn("Ignoring $file from archive since it is not listed in $plugin stateFiles.");
                }
            }
        }
    }
    $self->reConfigure;
}

=head2 $cfg->reConfigure()

Regenerate all the template based configuration files using input from the database.

=cut

sub reConfigure {
    my $self = shift;
    my $secretFile = $self->secretFile;
    if (not -f $secretFile){
        open my $rand, '>', $secretFile;
        chmod 0600,$secretFile;
        print $rand sprintf('%x%x',int(rand()*1e14),int(rand()*1e14));
        close $rand;
        chmod 0400,$secretFile;
    }
    for my $obj (@{$self->configPlugins}){
        $obj->reConfigure;
    }
}

=head2 $cfg->unConfigure()

Restore the system to unconfigured state. By removing the
configuration database, unlinking all user supplied configuration
files and regenerating all template based configuration files with
empty input.

=cut

sub unConfigure {
    no autodie;
    my $self = shift;
    my $cfg = $self->cfgHash;
    $self->log->debug("unlinking config database ".$cfg->{BACKEND}{cfg_db});
    unlink $cfg->{BACKEND}{cfg_db} if -f $cfg->{BACKEND}{cfg_db};
    open my $gen, '>', $cfg->{BACKEND}{cfg_db}.'.flush';
    close $gen;
    #get 'clean' config files
    $self->reConfigure();
    # and now remove all state
    for my $obj (@{$self->configPlugins}){
        for my $file (@{$obj->stateFiles},@{$obj->unConfigureFiles}) {
            next if not -f $file;
            $self->log->debug('['.$obj->name."] unlinking $file");
            unlink $file;
        }
    }
    unlink $cfg->{BACKEND}{log_file} if defined $cfg->{BACKEND}{log_file} and -f $cfg->{BACKEND}{log_file} ;
    unlink $self->secretFile if -f $self->secretFile;
    system "sync";
}

=head2 $cfg->promisify(xxx)

always return a promise resolving to the value

=cut

sub promisify {
    my $self = shift;
    my $value = shift;
    if (eval { blessed $value && $value->isa('Mojo::Promise') }){
        return $value;
    }

    return Mojo::Promise->resolve($value);
}

=head2 $cfg->promiseDeath(xxx)

die when there is a promise response

=cut



( run in 0.827 second using v1.01-cache-2.11-cpan-39bf76dae61 )