Footprintless

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

1.25 2017-07-10

    - resolved https://github.com/lucastheisen/footprintless/issues/4 get_entity fails if value is falsey

1.24 2017-03-16

    - resolved https://github.com/lucastheisen/footprintless/issues/3 Add support for one action to be applied to multiple services in one call

1.23 2016-12-16

    - fixed multi-template support for resource based overlay

1.22 2016-12-16

    - added resource based overlay support

1.21 2016-11-18

    - added sorting to config dumper and changed json default to json3

1.20 2016-10-21

    - added support for resource manager provider configuration

1.19 2016-10-20

    - added support for clean in .footprintless placeholders for overlay update

1.18 2016-10-12

    - resolved https://rt.cpan.org/Ticket/Display.html?id=118019 Can't redeclare "my" in "my"
    - resolved https://rt.cpan.org/Ticket/Display.html?id=118162 Using File::Temp::HIGH() is problematic
    - resolved https://rt.cpan.org/Ticket/Display.html?id=114358 Fails with perls < 5.14

1.17 2016-10-05

    - fixed bug causing footprintless to get initialized 3 times

Changes  view on Meta::CPAN

    - added ActionCommand and Action to cut down on boilerplate
    - added MixableBase to cut down on boilerplate initialization
    - added configurable tries and wait_seconds for tunnel startup
    - modified tunnel init to allow options to override entity for config

1.10 2016-08-20
    - added Footprintless::Extract implementation

1.09 2016-08-19
    - added support for extract_to in resources for Deployment
    - fixed overlay to use dot footprintless resolver on update as well as initialize
    - added prefix to temp_[dir/file] to provide easier cleanup in case process exits abnormally

1.08 2016-08-18
    - changed Footprintless::agent from a singleton to a factory

1.07 2016-08-17
    - fixed deployment extra to supply actual to_dir even if there is a resource_dir

1.06 2016-08-02
    - added documentation for deploy option 'extra'

Changes  view on Meta::CPAN

    - cleaned up dumper
    - added pod documentation to tunnel

1.04 2016-07-21
    - added ability to pass runner options to service
    - fixed logger message in Footprintless::_init
    - fixed Footprintless_App_Command_log logger to use STDERR to avoid STDOUT conflicts
    - fixed out_handle for until to print a newline after each line
    - modified temp_dir and added temp_file both of which are now secure, reimplemented service status to better handle su
    - added missing dependency on URI
    - added resolution during overlay if .footprintless is encountered
    - updated service to support sub commands with sudo if needed
    - updated pod for Footprintless::Util::slurp
    - fixed invalid variable reference in Footprintless::Util::exit_due_to, and added support for STDIN in Footprintless::
    - added Tunnel.pm and associated factory methods
    - updated tests to included --no-overwrite-dir for the cp_command tar
    - added --no-overwrite-dir to the cp_command tar to avoid needing permissions on folders we are not adding to/deleting

1.03 2016-04-30
    - Fixed Bug #113348 for Footprintless: Undeclared dependency App::Cmd::Tester

MANIFEST  view on Meta::CPAN

docs/Footprintless-App-Command-config.html
docs/Footprintless-App-Command-deployment-clean.html
docs/Footprintless-App-Command-deployment-deploy.html
docs/Footprintless-App-Command-deployment.html
docs/Footprintless-App-Command-log-cat.html
docs/Footprintless-App-Command-log-follow.html
docs/Footprintless-App-Command-log-grep.html
docs/Footprintless-App-Command-log-head.html
docs/Footprintless-App-Command-log-tail.html
docs/Footprintless-App-Command-log.html
docs/Footprintless-App-Command-overlay-clean.html
docs/Footprintless-App-Command-overlay-initialize.html
docs/Footprintless-App-Command-overlay-update.html
docs/Footprintless-App-Command-overlay.html
docs/Footprintless-App-Command-service.html
docs/Footprintless-App-DocumentationUtil.html
docs/Footprintless-App-UsageException.html
docs/Footprintless-App.html
docs/Footprintless-Command.html
docs/Footprintless-CommandOptionsFactory.html
docs/Footprintless-CommandRunner-ExecutionException.html
docs/Footprintless-CommandRunner-IPCRun.html
docs/Footprintless-CommandRunner-IPCRun3.html
docs/Footprintless-CommandRunner.html

MANIFEST  view on Meta::CPAN

lib/Footprintless/App/Command/config.pm
lib/Footprintless/App/Command/deployment.pm
lib/Footprintless/App/Command/deployment/clean.pm
lib/Footprintless/App/Command/deployment/deploy.pm
lib/Footprintless/App/Command/log.pm
lib/Footprintless/App/Command/log/cat.pm
lib/Footprintless/App/Command/log/follow.pm
lib/Footprintless/App/Command/log/grep.pm
lib/Footprintless/App/Command/log/head.pm
lib/Footprintless/App/Command/log/tail.pm
lib/Footprintless/App/Command/overlay.pm
lib/Footprintless/App/Command/overlay/clean.pm
lib/Footprintless/App/Command/overlay/initialize.pm
lib/Footprintless/App/Command/overlay/update.pm
lib/Footprintless/App/Command/service.pm
lib/Footprintless/App/DocumentationUtil.pm
lib/Footprintless/App/UsageException.pm
lib/Footprintless/Command.pm
lib/Footprintless/CommandOptionsFactory.pm
lib/Footprintless/CommandRunner.pm
lib/Footprintless/CommandRunner/ExecutionException.pm
lib/Footprintless/CommandRunner/IPCRun.pm
lib/Footprintless/CommandRunner/IPCRun3.pm
lib/Footprintless/Deployment.pm

MANIFEST  view on Meta::CPAN

lib/Footprintless/ResourceManager.pm
lib/Footprintless/Service.pm
lib/Footprintless/Tunnel.pm
lib/Footprintless/Util.pm
t/00-report-prereqs.dd
t/00-report-prereqs.t
t/Footprintless.t
t/Footprintless_App_Command_config.t
t/Footprintless_App_Command_deploy.t
t/Footprintless_App_Command_log.t
t/Footprintless_App_Command_overlay.t
t/Footprintless_App_Command_service.t
t/Footprintless_Command.t
t/Footprintless_CommandOptionsFactory.t
t/Footprintless_CommandRunner.t
t/Footprintless_Deployment.t
t/Footprintless_Extract.t
t/Footprintless_Localhost.t
t/Footprintless_Log.t
t/Footprintless_Overlay.t
t/Footprintless_Plugin.t

MANIFEST  view on Meta::CPAN

t/data/maven/HOME/dot_m2/settings.xml
t/data/resources/bar.tar
t/data/resources/bar.tgz
t/data/resources/bar.war
t/data/resources/bar/META-INF/MANIFEST.MF
t/data/resources/bar/META-INF/maven/com.pastdev/bar/pom.properties
t/data/resources/bar/META-INF/maven/com.pastdev/bar/pom.xml
t/data/resources/bar/WEB-INF/web.xml
t/data/resources/bar/index.html
t/data/resources/baz.war
t/data/resources/overlay.zip
t/data/resources/overlay/base/foo.txt
t/data/resources/overlay/template/first/bar.txt
t/data/resources/overlay/template/first/baz.txt
t/data/resources/overlay/template/second/bar.txt
t/data/template/bin/catalina.sh
t/data/template/bin/setenv.sh
t/data/template/conf/jndi-resources.xml
t/data/template/conf/server.xml
t/lib/Footprintless/CommandRunner/Echo.pm
t/lib/Footprintless/CommandRunner/Mock.pm
t/lib/Footprintless/EchoCommandRunnerTestFactory.pm
t/lib/Footprintless/ExtendedTestFactory.pm
t/lib/Footprintless/Test/Echo.pm
t/lib/Footprintless/Test/EchoPlugin.pm

META.json  view on Meta::CPAN

         "version" : "1.29"
      },
      "Footprintless::App::Command::log::head" : {
         "file" : "lib/Footprintless/App/Command/log/head.pm",
         "version" : "1.29"
      },
      "Footprintless::App::Command::log::tail" : {
         "file" : "lib/Footprintless/App/Command/log/tail.pm",
         "version" : "1.29"
      },
      "Footprintless::App::Command::overlay" : {
         "file" : "lib/Footprintless/App/Command/overlay.pm",
         "version" : "1.29"
      },
      "Footprintless::App::Command::overlay::clean" : {
         "file" : "lib/Footprintless/App/Command/overlay/clean.pm",
         "version" : "1.29"
      },
      "Footprintless::App::Command::overlay::initialize" : {
         "file" : "lib/Footprintless/App/Command/overlay/initialize.pm",
         "version" : "1.29"
      },
      "Footprintless::App::Command::overlay::update" : {
         "file" : "lib/Footprintless/App/Command/overlay/update.pm",
         "version" : "1.29"
      },
      "Footprintless::App::Command::service" : {
         "file" : "lib/Footprintless/App/Command/service.pm",
         "version" : "1.29"
      },
      "Footprintless::App::DocumentationUtil" : {
         "file" : "lib/Footprintless/App/DocumentationUtil.pm",
         "version" : "1.29"
      },

META.yml  view on Meta::CPAN

    version: '1.29'
  Footprintless::App::Command::log::grep:
    file: lib/Footprintless/App/Command/log/grep.pm
    version: '1.29'
  Footprintless::App::Command::log::head:
    file: lib/Footprintless/App/Command/log/head.pm
    version: '1.29'
  Footprintless::App::Command::log::tail:
    file: lib/Footprintless/App/Command/log/tail.pm
    version: '1.29'
  Footprintless::App::Command::overlay:
    file: lib/Footprintless/App/Command/overlay.pm
    version: '1.29'
  Footprintless::App::Command::overlay::clean:
    file: lib/Footprintless/App/Command/overlay/clean.pm
    version: '1.29'
  Footprintless::App::Command::overlay::initialize:
    file: lib/Footprintless/App/Command/overlay/initialize.pm
    version: '1.29'
  Footprintless::App::Command::overlay::update:
    file: lib/Footprintless/App/Command/overlay/update.pm
    version: '1.29'
  Footprintless::App::Command::service:
    file: lib/Footprintless/App/Command/service.pm
    version: '1.29'
  Footprintless::App::DocumentationUtil:
    file: lib/Footprintless/App/DocumentationUtil.pm
    version: '1.29'
  Footprintless::App::UsageException:
    file: lib/Footprintless/App/UsageException.pm
    version: '1.29'

README  view on Meta::CPAN


    version 1.29

SYNOPSIS

        use Footprintless;
    
        my $footprintless = Footprintless->new();
    
        # Deploy initialize, start, and follow the log of the foo
        $footprintless->overlay('dev.foo.overlay')->initialize();
        $footprintless->service('dev.foo.service')->start();
        $footprintless->log('dev.foo.logs.app')->follow();

DESCRIPTION

    Footprintless is an automation framework with an application frontend
    for managing diverse software stacks in a consistent fashion. It
    provides a minimally invasive approach to configuration management. At
    its core, Config::Entities are used to define the whole system
    <https://en.wikipedia.org/wiki/System>. Once defined, the entities are

README  view on Meta::CPAN

                    resources => {
                        bar => 'com.pastdev:bar:war:1.0',
                        baz => 'com.pastdev:baz:war:1.0'
                    },
                    to_dir => '/opt/foo/tomcat/webapps'
                },
                hostname => 'app.pastdev.com',
                logs => {
                    catalina => '/opt/foo/tomcat/logs/catalina.out'
                },
                overlay => {
                    'Config::Entities::inherit' => ['hostname', 'sudo_username'],
                    base_dir => '/home/me/git/foo/base',
                    clean => [
                        '/opt/foo/tomcat/'
                    ],
                    deployment_coordinate => 'foo.dev.app.deployment',
                    key => 'T',
                    os => 'linux',
                    resolver_coordinate => 'foo.dev',
                    template_dir => '/home/me/git/foo/template',

README  view on Meta::CPAN

    command_runner

      A command_runner to use instead of that which is supplied by this
      footprintless instance.

    localhost

      A localhost to use instead of that which is supplied by this
      footprintless instance.

 overlay($coordinate, %options)

    Returns a new instance of Footprintless::Overlay preconfigured to
    operate on the overlay at $coordinate. Supported options are

    command_options_factory

      A command_options_factory to use instead of that which is supplied by
      this footprintless instance.

    command_runner

      A command_runner to use instead of that which is supplied by this
      footprintless instance.

docs/Footprintless-App-Command-overlay-clean.html  view on Meta::CPAN

<html><head><title>Footprintless::App::Command::overlay::clean</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" >

<style type="text/css">
 <!--/*--><![CDATA[/*><!--*/
BODY {
  background: white;
  color: black;
  font-family: arial,sans-serif;
  margin: 0;
  padding: 1ex;

docs/Footprintless-App-Command-overlay-clean.html  view on Meta::CPAN

  <li class='indexItem indexItem1'><a href='#AUTHOR'>AUTHOR</a>
  <li class='indexItem indexItem1'><a href='#COPYRIGHT_AND_LICENSE'>COPYRIGHT AND LICENSE</a>
  <li class='indexItem indexItem1'><a href='#SEE_ALSO'>SEE ALSO</a>
</ul>
</div>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="NAME"
>NAME</a></h1>

<p>Footprintless::App::Command::overlay::clean - removes all files managed by the overlay</p>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="VERSION"
>VERSION</a></h1>

<p>version 1.29</p>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="AUTHOR"
>AUTHOR</a></h1>

docs/Footprintless-App-Command-overlay-initialize.html  view on Meta::CPAN

<html><head><title>Footprintless::App::Command::overlay::initialize</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" >

<style type="text/css">
 <!--/*--><![CDATA[/*><!--*/
BODY {
  background: white;
  color: black;
  font-family: arial,sans-serif;
  margin: 0;
  padding: 1ex;

docs/Footprintless-App-Command-overlay-initialize.html  view on Meta::CPAN

  <li class='indexItem indexItem1'><a href='#AUTHOR'>AUTHOR</a>
  <li class='indexItem indexItem1'><a href='#COPYRIGHT_AND_LICENSE'>COPYRIGHT AND LICENSE</a>
  <li class='indexItem indexItem1'><a href='#SEE_ALSO'>SEE ALSO</a>
</ul>
</div>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="NAME"
>NAME</a></h1>

<p>Footprintless::App::Command::overlay::initialize - cleans,
then processes the overlay base and template files</p>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="VERSION"
>VERSION</a></h1>

<p>version 1.29</p>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="AUTHOR"
>AUTHOR</a></h1>

docs/Footprintless-App-Command-overlay-update.html  view on Meta::CPAN

<html><head><title>Footprintless::App::Command::overlay::clean</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" >

<style type="text/css">
 <!--/*--><![CDATA[/*><!--*/
BODY {
  background: white;
  color: black;
  font-family: arial,sans-serif;
  margin: 0;
  padding: 1ex;

docs/Footprintless-App-Command-overlay-update.html  view on Meta::CPAN

  <li class='indexItem indexItem1'><a href='#AUTHOR'>AUTHOR</a>
  <li class='indexItem indexItem1'><a href='#COPYRIGHT_AND_LICENSE'>COPYRIGHT AND LICENSE</a>
  <li class='indexItem indexItem1'><a href='#SEE_ALSO'>SEE ALSO</a>
</ul>
</div>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="NAME"
>NAME</a></h1>

<p>Footprintless::App::Command::overlay::clean - processes the overlay template files only</p>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="VERSION"
>VERSION</a></h1>

<p>version 1.29</p>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="AUTHOR"
>AUTHOR</a></h1>

docs/Footprintless-App-Command-overlay.html  view on Meta::CPAN

<html><head><title>Footprintless::App::Command::overlay</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" >

<style type="text/css">
 <!--/*--><![CDATA[/*><!--*/
BODY {
  background: white;
  color: black;
  font-family: arial,sans-serif;
  margin: 0;
  padding: 1ex;

docs/Footprintless-App-Command-overlay.html  view on Meta::CPAN

  <li class='indexItem indexItem1'><a href='#AUTHOR'>AUTHOR</a>
  <li class='indexItem indexItem1'><a href='#COPYRIGHT_AND_LICENSE'>COPYRIGHT AND LICENSE</a>
  <li class='indexItem indexItem1'><a href='#SEE_ALSO'>SEE ALSO</a>
</ul>
</div>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="NAME"
>NAME</a></h1>

<p>Footprintless::App::Command::overlay - Performs an action on an overlay.</p>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="VERSION"
>VERSION</a></h1>

<p>version 1.29</p>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="SYNOPSIS"
>SYNOPSIS</a></h1>

<pre>  fpl overlay OVERLAY_COORD clean
  fpl overlay OVERLAY_COORD initialize
  fpl overlay OVERLAY_COORD update
  fpl overlay OVERLAY_COORD # same as update</pre>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="DESCRIPTION"
>DESCRIPTION</a></h1>

<p>Performs actions on an overlay.</p>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="AUTHOR"
>AUTHOR</a></h1>

<p>Lucas Theisen &#60;lucastheisen@pastdev.com&#62;</p>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="COPYRIGHT_AND_LICENSE"
>COPYRIGHT AND LICENSE</a></h1>

docs/Footprintless-App-Command-overlay.html  view on Meta::CPAN


<p>Please see those modules/websites for more information related to this module.</p>

<ul>
<li><a href="http://search.cpan.org/perldoc?Footprintless" class="podlinkpod"
>Footprintless</a></li>

<li><a href="http://search.cpan.org/perldoc?Footprintless%3A%3AOverlay" class="podlinkpod"
>Footprintless::Overlay</a></li>

<li><a href="http://search.cpan.org/perldoc?Footprintless%3A%3AApp%3A%3ACommand%3A%3Aoverlay%3A%3Aclean" class="podlinkpod"
>Footprintless::App::Command::overlay::clean</a></li>

<li><a href="http://search.cpan.org/perldoc?Footprintless%3A%3AApp%3A%3ACommand%3A%3Aoverlay%3A%3Ainitialize" class="podlinkpod"
>Footprintless::App::Command::overlay::initialize</a></li>

<li><a href="http://search.cpan.org/perldoc?Footprintless%3A%3AApp%3A%3ACommand%3A%3Aoverlay%3A%3Aupdate" class="podlinkpod"
>Footprintless::App::Command::overlay::update</a></li>
</ul>

<!-- end doc -->

</body></html>

docs/Footprintless-Factory.html  view on Meta::CPAN

  <li class='indexItem indexItem1'><a href='#METHODS'>METHODS</a>
  <ul   class='indexList indexList2'>
    <li class='indexItem indexItem2'><a href='#agent(%25options)'>agent(%options)</a>
    <li class='indexItem indexItem2'><a href='#command_options(%25spec)'>command_options(%spec)</a>
    <li class='indexItem indexItem2'><a href='#command_options_factory()'>command_options_factory()</a>
    <li class='indexItem indexItem2'><a href='#command_runner()'>command_runner()</a>
    <li class='indexItem indexItem2'><a href='#deployment(%24coordinate%2C_%25options)'>deployment($coordinate, %options)</a>
    <li class='indexItem indexItem2'><a href='#entities()'>entities()</a>
    <li class='indexItem indexItem2'><a href='#localhost()'>localhost()</a>
    <li class='indexItem indexItem2'><a href='#log(%24coordinate%2C_%25options)'>log($coordinate, %options)</a>
    <li class='indexItem indexItem2'><a href='#overlay(%24coordinate%2C_%25options)'>overlay($coordinate, %options)</a>
    <li class='indexItem indexItem2'><a href='#plugins()'>plugins()</a>
    <li class='indexItem indexItem2'><a href='#register_plugin(%24plugin)'>register_plugin($plugin)</a>
    <li class='indexItem indexItem2'><a href='#resource_manager()'>resource_manager()</a>
    <li class='indexItem indexItem2'><a href='#service(%24coordinate%2C_%25options)'>service($coordinate, %options)</a>
    <li class='indexItem indexItem2'><a href='#tunnel(%24coordinate%2C_%25options)'>tunnel($coordinate, %options)</a>
  </ul>
  <li class='indexItem indexItem1'><a href='#AUTHOR'>AUTHOR</a>
  <li class='indexItem indexItem1'><a href='#COPYRIGHT_AND_LICENSE'>COPYRIGHT AND LICENSE</a>
  <li class='indexItem indexItem1'><a href='#SEE_ALSO'>SEE ALSO</a>
</ul>

docs/Footprintless-Factory.html  view on Meta::CPAN


<dt><a name="localhost"
>localhost</a></dt>

<dd>
<p>A <code>localhost</code> to use instead of that which is supplied by this footprintless instance.</p>
</dd>
</dl>

<h2><a class='u' href='#___top' title='click to go to top of document'
name="overlay($coordinate,_%options)"
>overlay($coordinate,
%options)</a></h2>

<p>Returns a new instance of <a href="http://search.cpan.org/perldoc?Footprintless%3A%3AOverlay" class="podlinkpod"
>Footprintless::Overlay</a> preconfigured to operate on the overlay at <code>$coordinate</code>.
Supported options are</p>

<dl>
<dt><a name="command_options_factory"
>command_options_factory</a></dt>

<dd>
<p>A <code>command_options_factory</code> to use instead of that which is supplied by this footprintless instance.</p>

<dt><a name="command_runner"

docs/Footprintless-Mixins.html  view on Meta::CPAN

  <li class='indexItem indexItem1'><a href='#VERSION'>VERSION</a>
  <li class='indexItem indexItem1'><a href='#DESCRIPTION'>DESCRIPTION</a>
  <li class='indexItem indexItem1'><a href='#EXPORT_OK'>EXPORT_OK</a>
  <ul   class='indexList indexList2'>
    <li class='indexItem indexItem2'><a href='#_command_options(%5B%24sub_coordinate%5D)'>_command_options([$sub_coordinate])</a>
    <li class='indexItem indexItem2'><a href='#_deployment(%24sub_coordinate%2C_%5B%24name%5D)'>_deployment($sub_coordinate, [$name])</a>
    <li class='indexItem indexItem2'><a href='#_download(%24resource%2C_%5B%24to%5D)'>_download($resource, [$to])</a>
    <li class='indexItem indexItem2'><a href='#_entity(%24coordinate%2C_%5B%24required%5D)'>_entity($coordinate, [$required])</a>
    <li class='indexItem indexItem2'><a href='#_is_local(%24hostname_sub_coordinate)'>_is_local($hostname_sub_coordinate)</a>
    <li class='indexItem indexItem2'><a href='#_local_template(%5C%26local_work%2C_%5B%25options%5D)'>_local_template(\&#38;local_work, [%options])</a>
    <li class='indexItem indexItem2'><a href='#_overlay(%24sub_coordinate%2C_%5B%24name%5D)'>_overlay($sub_coordinate, [$name])</a>
    <li class='indexItem indexItem2'><a href='#_push_resource_to_destination(%24source%2C_%24destination%2C_%5B%25options%5D)'>_push_resource_to_destination($source, $destination, [%options])</a>
    <li class='indexItem indexItem2'><a href='#_push_to_destination(%24source%2C_%24destination%2C_%5B%25options%5D)'>_push_to_destination($source, $destination, [%options])</a>
    <li class='indexItem indexItem2'><a href='#_resolve(%24resource)'>_resolve($resource)</a>
    <li class='indexItem indexItem2'><a href='#_service(%24sub_coordinate%2C_%5B%24name%5D)'>_service($sub_coordinate, [$name])</a>
    <li class='indexItem indexItem2'><a href='#_sub_coordinate(%24sub_coordinate)'>_sub_coordinate($sub_coordinate)</a>
    <li class='indexItem indexItem2'><a href='#_sub_entity(%24sub_coordinate%2C_%5B%24required%5D)'>_sub_entity($sub_coordinate, [$required])</a>
    <li class='indexItem indexItem2'><a href='#_verify_required_entities(%40sub_coordinates)'>_verify_required_entities(@sub_coordinates)</a>
  </ul>
  <li class='indexItem indexItem1'><a href='#AUTHOR'>AUTHOR</a>
  <li class='indexItem indexItem1'><a href='#COPYRIGHT_AND_LICENSE'>COPYRIGHT AND LICENSE</a>

docs/Footprintless-Mixins.html  view on Meta::CPAN

<p>Returns a truthy value if the value at <code>$hostname_sub_coordinate</code> is an alias for the local system.</p>

<h2><a class='u' href='#___top' title='click to go to top of document'
name="_local_template(\&#38;local_work,_[%options])"
>_local_template(\&#38;local_work, [%options])</a></h2>

<p>Will check if the entity at <code>$self-&#62;{coordinate}</code> has a <code>hostname</code> that is local, and if not, it will create a temp directory, call <code>&#38;local_work</code> with that directory, and call <a href="http://search.cpan.or...
>&#34;_push_to_destination($source, $destination, [%options])&#34; in _push_to_destination</a> with the temp directory as the source, and <code>to_dir</code> as the destination when complete. If <code>hostname</code> is <i>not</i> local, then <code>&...

<h2><a class='u' href='#___top' title='click to go to top of document'
name="_overlay($sub_coordinate,_[$name])"
>_overlay($sub_coordinate, [$name])</a></h2>

<p>Returns a <code>overlay</code> from the factory. If <code>$name</code> is supplied, the <code>overlay</code> will be a singleton stored at <code>$self-</code>{$name}&#62;.</p>

<h2><a class='u' href='#___top' title='click to go to top of document'
name="_push_resource_to_destination($source,_$destination,_[%options])"
>_push_resource_to_destination($source, $destination, [%options])</a></h2>

<p>Pushes <code>$source</code> to <code>$destination</code>. Supported options are:</p>

<dl>
<dt><a name="extract"
>extract</a></dt>

docs/Footprintless-Overlay.html  view on Meta::CPAN

  <li class='indexItem indexItem1'><a href='#COPYRIGHT_AND_LICENSE'>COPYRIGHT AND LICENSE</a>
  <li class='indexItem indexItem1'><a href='#SEE_ALSO'>SEE ALSO</a>
  <li class='indexItem indexItem1'><a href='#CONFIGURATION'>CONFIGURATION</a>
</ul>
</div>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="NAME"
>NAME</a></h1>

<p>Footprintless::Overlay - An overlay manager</p>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="VERSION"
>VERSION</a></h1>

<p>version 1.29</p>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="SYNOPSIS"
>SYNOPSIS</a></h1>

<pre>    # Standard way of getting an overlay
    use Footprintless;
    my $overlay = Footprintless-&#62;new()-&#62;overlay(&#39;overlay&#39;);

    $overlay-&#62;clean();

    $overlay-&#62;initialize();

    $overlay-&#62;update();</pre>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="DESCRIPTION"
>DESCRIPTION</a></h1>

<p>Overlays are a combination of a directory of static files and a directory of templated files that will be merged to an output directory. This is implemented in <a href="http://search.cpan.org/perldoc?Template%3A%3AOverlay" class="podlinkpod"
>Template::Overlay</a>.</p>

<p>Additionally, any folder under the <code>template_dir</code> can contain a <code>.footprintless</code> file containing a <code>clean</code> and/or <code>resources</code> entities:</p>

docs/Footprintless-Overlay.html  view on Meta::CPAN

            bar =&#62; &#39;com.pastdev:bar:1.0.0&#39;
        }
    };</pre>

<p>The <code>clean</code> entity is an arrayref containing a list of paths to clean out. These paths will be added to the path of the directory containing the <code>.footprintless</code> file. The <code>resources</code> entity is a list of resources ...

<h1><a class='u' href='#___top' title='click to go to top of document'
name="ENTITIES"
>ENTITIES</a></h1>

<p>A simple overlay:</p>

<pre>    overlay =&#62; {
        base_dir =&#62; &#34;/home/me/foo/base&#34;,
        clean =&#62; [
            &#34;/opt/tomcat/&#34;
        ],
        hostname =&#62; &#39;localhost&#39;,
        key =&#62; &#39;T&#39;,
        os =&#62; &#39;linux&#39;,
        template_dir =&#62; &#34;/home/me/foo/template&#34;,
        to_dir =&#62; &#39;/opt/foo/tomcat&#39;
    }</pre>

<p>A more complex example:</p>

<pre>    foo =&#62; {
        hostname =&#62; &#39;test.pastdev.com&#39;,
        overlay =&#62; {
            &#39;Config::Entities::inherit&#39; =&#62; [&#39;hostname&#39;, &#39;sudo_username&#39;],
            base_dir =&#62; &#39;/home/me/foo/base&#39;,
            clean =&#62; [
                &#39;/opt/foo/tomcat/&#39;
            ],
            key =&#62; &#39;T&#39;,
            os =&#62; &#39;linux&#39;,
            resolver_coordinate =&#62; &#39;foo&#39;,
            template_dir =&#62; &#39;/home/me/foo/template&#39;,
            to_dir =&#62; &#39;/opt/foo/tomcat&#39;

docs/Footprintless-Overlay.html  view on Meta::CPAN

            },
            trust_store =&#62; {
                &#39;Config::Entities::inherit&#39; =&#62; [&#39;hostname&#39;, &#39;sudo_username&#39;],
                file =&#62; &#39;/opt/foo/tomcat/certs/truststore.jks&#39;,
                include_java_home_cacerts =&#62; 1,
                password =&#62; $properties-&#62;{&#39;foo.tomcat.trust_store.password&#39;},
            }
        }
    }</pre>

<p>An overlay can obtain base/template content from a resource. When initialize or update are called, the resource will be downloaded (if not already local) and extracted to a temp folder. The <code>base_dir</code> and <code>template_dir</code> paths...

<pre>    overlay =&#62; {
        base_dir =&#62; &#39;base&#39;,
        clean =&#62; [
            &#39;/opt/tomcat/&#39;
        ],
        hostname =&#62; &#39;localhost&#39;,
        key =&#62; &#39;T&#39;,
        os =&#62; &#39;linux&#39;,
        resource =&#62; &#39;com.pastdev:app-overlay:zip:package:1.0.0&#39;,
        template_dir =&#62; &#39;template&#39;,
        to_dir =&#62; &#39;/opt/foo/tomcat&#39;
    }</pre>

<p>An overlay can have multiple template folders. If it does, they will be processed in the order they are listed:</p>

<pre>    overlay =&#62; {
        base_dir =&#62; &#39;base&#39;,
        clean =&#62; [
            &#39;/opt/tomcat/&#39;
        ],
        hostname =&#62; &#39;localhost&#39;,
        key =&#62; &#39;T&#39;,
        os =&#62; &#39;linux&#39;,
        template_dir =&#62; [
            &#39;first/template_dir&#39;,
            &#39;second/template_dir&#39;,

docs/Footprintless-Overlay.html  view on Meta::CPAN

    }</pre>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="CONSTRUCTORS"
>CONSTRUCTORS</a></h1>

<h2><a class='u' href='#___top' title='click to go to top of document'
name="new($entity,_$coordinate)"
>new($entity, $coordinate)</a></h2>

<p>Constructs a new overlay configured by <code>$entities</code> at <code>$coordinate</code>.</p>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="METHODS"
>METHODS</a></h1>

<h2><a class='u' href='#___top' title='click to go to top of document'
name="clean()"
>clean()</a></h2>

<p>Cleans the overlay. Each path in the <code>clean</code> entity, will be removed from the destination. If the path ends in a <code>/</code>, then after being removed, the directory will be recreated.</p>

<h2><a class='u' href='#___top' title='click to go to top of document'
name="initialize()"
>initialize()</a></h2>

<p>Will call <code>clean</code>, then <code>overlay</code> on an instance of <a href="http://search.cpan.org/perldoc?Template%3A%3AOverlay" class="podlinkpod"
>Template::Overlay</a> configured to this entity.</p>

<h2><a class='u' href='#___top' title='click to go to top of document'
name="update()"
>update()</a></h2>

<p>Will overlay <i>ONLY</i> the templated files. It will not <code>clean</code>, nor copy any files from <code>base_dir</code> like <code>initialize</code> does.</p>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="AUTHOR"
>AUTHOR</a></h1>

<p>Lucas Theisen &#60;lucastheisen@pastdev.com&#62;</p>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="COPYRIGHT_AND_LICENSE"
>COPYRIGHT AND LICENSE</a></h1>

docs/Footprintless-Overlay.html  view on Meta::CPAN

>Template::Resolver</a></li>
</ul>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="CONFIGURATION"
>CONFIGURATION</a></h1>

<p>This module can optionally be configured to use a customized resolver. To do so, configure a resolver factory in your entities:</p>

<pre>    footprintless =&#62; {
        overlay =&#62; {
            resolver_factory =&#62; &#39;My::ResolverFactory&#39;
        }
    }</pre>

<p>The resolver factory must have a <code>new_resolver</code> method that takes a spec and a list of options and returns a <code>Template::Resolver</code>, for example:</p>

<pre>    sub new_resolver {
        my ($self, $resolver_spec, %resolver_opts) = @_;
        return Template::Resolver-&#62;new(
            $resolver_spec,

docs/Footprintless.html  view on Meta::CPAN

  </ul>
  <li class='indexItem indexItem1'><a href='#METHODS'>METHODS</a>
  <ul   class='indexList indexList2'>
    <li class='indexItem indexItem2'><a href='#agent(%25options)'>agent(%options)</a>
    <li class='indexItem indexItem2'><a href='#command_options_factory()'>command_options_factory()</a>
    <li class='indexItem indexItem2'><a href='#command_runner()'>command_runner()</a>
    <li class='indexItem indexItem2'><a href='#deployment(%24coordinate%2C_%25options)'>deployment($coordinate, %options)</a>
    <li class='indexItem indexItem2'><a href='#entities()'>entities()</a>
    <li class='indexItem indexItem2'><a href='#localhost()'>localhost()</a>
    <li class='indexItem indexItem2'><a href='#log(%24coordinate%2C_%25options)'>log($coordinate, %options)</a>
    <li class='indexItem indexItem2'><a href='#overlay(%24coordinate%2C_%25options)'>overlay($coordinate, %options)</a>
    <li class='indexItem indexItem2'><a href='#plugins()'>plugins()</a>
    <li class='indexItem indexItem2'><a href='#resource_manager()'>resource_manager()</a>
    <li class='indexItem indexItem2'><a href='#service(%24coordinate%2C_%25options)'>service($coordinate, %options)</a>
    <li class='indexItem indexItem2'><a href='#tunnel(%24coordinate%2C_%25options)'>tunnel($coordinate, %options)</a>
  </ul>
  <li class='indexItem indexItem1'><a href='#AUTHOR'>AUTHOR</a>
  <li class='indexItem indexItem1'><a href='#COPYRIGHT_AND_LICENSE'>COPYRIGHT AND LICENSE</a>
  <li class='indexItem indexItem1'><a href='#SEE_ALSO'>SEE ALSO</a>
</ul>
</div>

docs/Footprintless.html  view on Meta::CPAN


<h1><a class='u' href='#___top' title='click to go to top of document'
name="SYNOPSIS"
>SYNOPSIS</a></h1>

<pre>    use Footprintless;

    my $footprintless = Footprintless-&#62;new();

    # Deploy initialize, start, and follow the log of the foo
    $footprintless-&#62;overlay(&#39;dev.foo.overlay&#39;)-&#62;initialize();
    $footprintless-&#62;service(&#39;dev.foo.service&#39;)-&#62;start();
    $footprintless-&#62;log(&#39;dev.foo.logs.app&#39;)-&#62;follow();</pre>

<h1><a class='u' href='#___top' title='click to go to top of document'
name="DESCRIPTION"
>DESCRIPTION</a></h1>

<p>Footprintless is an automation framework with an application frontend for managing diverse software stacks in a consistent fashion. It provides a minimally invasive approach to configuration management. At its core, <a href="http://search.cpan.org...
>Config::Entities</a> are used to define the whole <a href="https://en.wikipedia.org/wiki/System" class="podlinkurl"
>system</a>. Once defined, the entities are used by all of the Footprintless modules to decouple the environment from the action. The environment is defined by the entities used to create <a href="http://search.cpan.org/perldoc?Footprintless%3A%3ACom...

docs/Footprintless.html  view on Meta::CPAN

                resources =&#62; {
                    bar =&#62; &#39;com.pastdev:bar:war:1.0&#39;,
                    baz =&#62; &#39;com.pastdev:baz:war:1.0&#39;
                },
                to_dir =&#62; &#39;/opt/foo/tomcat/webapps&#39;
            },
            hostname =&#62; &#39;app.pastdev.com&#39;,
            logs =&#62; {
                catalina =&#62; &#39;/opt/foo/tomcat/logs/catalina.out&#39;
            },
            overlay =&#62; {
                &#39;Config::Entities::inherit&#39; =&#62; [&#39;hostname&#39;, &#39;sudo_username&#39;],
                base_dir =&#62; &#39;/home/me/git/foo/base&#39;,
                clean =&#62; [
                    &#39;/opt/foo/tomcat/&#39;
                ],
                deployment_coordinate =&#62; &#39;foo.dev.app.deployment&#39;,
                key =&#62; &#39;T&#39;,
                os =&#62; &#39;linux&#39;,
                resolver_coordinate =&#62; &#39;foo.dev&#39;,
                template_dir =&#62; &#39;/home/me/git/foo/template&#39;,

docs/Footprintless.html  view on Meta::CPAN


<dt><a name="localhost"
>localhost</a></dt>

<dd>
<p>A <code>localhost</code> to use instead of that which is supplied by this footprintless instance.</p>
</dd>
</dl>

<h2><a class='u' href='#___top' title='click to go to top of document'
name="overlay($coordinate,_%options)"
>overlay($coordinate, %options)</a></h2>

<p>Returns a new instance of <a href="http://search.cpan.org/perldoc?Footprintless%3A%3AOverlay" class="podlinkpod"
>Footprintless::Overlay</a> preconfigured to operate on the overlay at <code>$coordinate</code>. Supported options are</p>

<dl>
<dt><a name="command_options_factory"
>command_options_factory</a></dt>

<dd>
<p>A <code>command_options_factory</code> to use instead of that which is supplied by this footprintless instance.</p>

<dt><a name="command_runner"
>command_runner</a></dt>

lib/Footprintless.pm  view on Meta::CPAN

sub log {
    my ( $self, @args ) = @_;
    $self->{factory}->log(@args);
}

sub plugins {
    my ($self) = @_;
    $self->{factory}->plugins();
}

sub overlay {
    my ( $self, @args ) = @_;
    $self->{factory}->overlay(@args);
}

sub resource_manager {
    my ( $self, @args ) = @_;
    $self->{factory}->resource_manager(@args);
}

sub service {
    my ( $self, @args ) = @_;
    $self->{factory}->service(@args);

lib/Footprintless.pm  view on Meta::CPAN


version 1.29

=head1 SYNOPSIS

    use Footprintless;

    my $footprintless = Footprintless->new();

    # Deploy initialize, start, and follow the log of the foo
    $footprintless->overlay('dev.foo.overlay')->initialize();
    $footprintless->service('dev.foo.service')->start();
    $footprintless->log('dev.foo.logs.app')->follow();

=head1 DESCRIPTION

Footprintless is an automation framework with an application frontend for
managing diverse software stacks in a consistent fashion.  It provides a
minimally invasive approach to configuration management.  At its core, 
L<Config::Entities> are used to define the whole
L<system|https://en.wikipedia.org/wiki/System>.  Once defined, the

lib/Footprintless.pm  view on Meta::CPAN

                resources => {
                    bar => 'com.pastdev:bar:war:1.0',
                    baz => 'com.pastdev:baz:war:1.0'
                },
                to_dir => '/opt/foo/tomcat/webapps'
            },
            hostname => 'app.pastdev.com',
            logs => {
                catalina => '/opt/foo/tomcat/logs/catalina.out'
            },
            overlay => {
                'Config::Entities::inherit' => ['hostname', 'sudo_username'],
                base_dir => '/home/me/git/foo/base',
                clean => [
                    '/opt/foo/tomcat/'
                ],
                deployment_coordinate => 'foo.dev.app.deployment',
                key => 'T',
                os => 'linux',
                resolver_coordinate => 'foo.dev',
                template_dir => '/home/me/git/foo/template',

lib/Footprintless.pm  view on Meta::CPAN

A C<command_runner> to use instead of that which is supplied by
this footprintless instance.

=item localhost

A C<localhost> to use instead of that which is supplied by
this footprintless instance.

=back

=head2 overlay($coordinate, %options)

Returns a new instance of L<Footprintless::Overlay> preconfigured to
operate on the overlay at C<$coordinate>.  Supported options are

=over 4

=item command_options_factory

A C<command_options_factory> to use instead of that which is supplied by
this footprintless instance.

=item command_runner

lib/Footprintless/App/Command/overlay.pm  view on Meta::CPAN

use strict;
use warnings;

package Footprintless::App::Command::overlay;
$Footprintless::App::Command::overlay::VERSION = '1.29';
# ABSTRACT: Performs an action on an overlay.
# PODNAME: Footprintless::App::Command::overlay

use parent qw(Footprintless::App::ActionCommand);

sub _actions {
    return (
        'clean'      => 'Footprintless::App::Command::overlay::clean',
        'initialize' => 'Footprintless::App::Command::overlay::initialize',
        'update'     => 'Footprintless::App::Command::overlay::update'
    );
}

sub _default_action {
    return 'update';
}

sub usage_desc {
    return "fpl overlay OVERLAY_COORD ACTION %o";
}

1;

__END__

=pod

=head1 NAME

Footprintless::App::Command::overlay - Performs an action on an overlay.

=head1 VERSION

version 1.29

=head1 SYNOPSIS

  fpl overlay OVERLAY_COORD clean
  fpl overlay OVERLAY_COORD initialize
  fpl overlay OVERLAY_COORD update
  fpl overlay OVERLAY_COORD # same as update

=head1 DESCRIPTION

Performs actions on an overlay. 

=head1 AUTHOR

Lucas Theisen <lucastheisen@pastdev.com>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2016 by Lucas Theisen.

This is free software; you can redistribute it and/or modify it under

lib/Footprintless/App/Command/overlay.pm  view on Meta::CPAN

=item *

L<Footprintless|Footprintless>

=item *

L<Footprintless::Overlay|Footprintless::Overlay>

=item *

L<Footprintless::App::Command::overlay::clean|Footprintless::App::Command::overlay::clean>

=item *

L<Footprintless::App::Command::overlay::initialize|Footprintless::App::Command::overlay::initialize>

=item *

L<Footprintless::App::Command::overlay::update|Footprintless::App::Command::overlay::update>

=back

=cut

lib/Footprintless/App/Command/overlay/clean.pm  view on Meta::CPAN

use strict;
use warnings;

package Footprintless::App::Command::overlay::clean;
$Footprintless::App::Command::overlay::clean::VERSION = '1.29';
# ABSTRACT: removes all files managed by the overlay
# PODNAME: Footprintless::App::Command::overlay::clean

use parent qw(Footprintless::App::Action);

use Footprintless::App -ignore;
use Log::Any;

my $logger = Log::Any->get_logger();

sub execute {
    my ( $self, $opts, $args ) = @_;

    $logger->info('Performing clean...');
    $self->{overlay}->clean();

    $logger->info('Done...');
}

sub usage_desc {
    return "fpl overlay OVERLAY_COORD clean";
}

sub validate_args {
    my ( $self, $opts, $args ) = @_;

    eval { $self->{overlay} = $self->{footprintless}->overlay( $self->{coordinate} ); };

    $self->usage_error("invalid coordinate [$self->{coordinate}]: $@") if ($@);
}

1;

__END__

=pod

=head1 NAME

Footprintless::App::Command::overlay::clean - removes all files managed by the overlay

=head1 VERSION

version 1.29

=head1 AUTHOR

Lucas Theisen <lucastheisen@pastdev.com>

=head1 COPYRIGHT AND LICENSE

lib/Footprintless/App/Command/overlay/initialize.pm  view on Meta::CPAN

use strict;
use warnings;

package Footprintless::App::Command::overlay::initialize;
$Footprintless::App::Command::overlay::initialize::VERSION = '1.29';
# ABSTRACT: cleans, then processes the overlay base and template files
# PODNAME: Footprintless::App::Command::overlay::initialize

use parent qw(Footprintless::App::Action);

use Footprintless::App -ignore;
use Log::Any;

my $logger = Log::Any->get_logger();

sub execute {
    my ( $self, $opts, $args ) = @_;

    $logger->info('Performing initialize...');
    $self->{overlay}->initialize();

    $logger->info('Done...');
}

sub usage_desc {
    return "fpl overlay OVERLAY_COORD initialize %o";
}

sub validate_args {
    my ( $self, $opts, $args ) = @_;

    eval { $self->{overlay} = $self->{footprintless}->overlay( $self->{coordinate} ); };

    $self->usage_error("invalid coordinate [$self->{coordinate}]: $@") if ($@);
}

1;

__END__

=pod

=head1 NAME

Footprintless::App::Command::overlay::initialize - cleans, then processes the overlay base and template files

=head1 VERSION

version 1.29

=head1 AUTHOR

Lucas Theisen <lucastheisen@pastdev.com>

=head1 COPYRIGHT AND LICENSE

lib/Footprintless/App/Command/overlay/update.pm  view on Meta::CPAN

use strict;
use warnings;

package Footprintless::App::Command::overlay::update;
$Footprintless::App::Command::overlay::update::VERSION = '1.29';
# ABSTRACT: processes the overlay template files only
# PODNAME: Footprintless::App::Command::overlay::clean

use parent qw(Footprintless::App::Action);

use Footprintless::App -ignore;
use Log::Any;

my $logger = Log::Any->get_logger();

sub execute {
    my ( $self, $opts, $args ) = @_;

    $logger->info('Performing update...');
    $self->{overlay}->update();

    $logger->info('Done...');
}

sub usage_desc {
    return "fpl overlay OVERLAY_COORD update %o";
}

sub validate_args {
    my ( $self, $opts, $args ) = @_;

    eval { $self->{overlay} = $self->{footprintless}->overlay( $self->{coordinate} ); };

    $self->usage_error("invalid coordinate [$self->{coordinate}]: $@") if ($@);
}

1;

__END__

=pod

=head1 NAME

Footprintless::App::Command::overlay::clean - processes the overlay template files only

=head1 VERSION

version 1.29

=head1 AUTHOR

Lucas Theisen <lucastheisen@pastdev.com>

=head1 COPYRIGHT AND LICENSE

lib/Footprintless/Factory.pm  view on Meta::CPAN

    return $self->{localhost};
}

sub log {
    my ( $self, $coordinate, %options ) = @_;

    require Footprintless::Log;
    return Footprintless::Log->new( $self, $coordinate, %options );
}

sub overlay {
    my ( $self, $coordinate, %options ) = @_;

    require Footprintless::Overlay;
    return Footprintless::Overlay->new( $self, $coordinate, %options );
}

sub plugins {
    return @{ $_[0]->{plugins} };
}

lib/Footprintless/Factory.pm  view on Meta::CPAN

A C<command_runner> to use instead of that which is supplied by
this footprintless instance.

=item localhost

A C<localhost> to use instead of that which is supplied by
this footprintless instance.

=back

=head2 overlay($coordinate, %options)

Returns a new instance of L<Footprintless::Overlay> preconfigured to
operate on the overlay at C<$coordinate>.  Supported options are

=over 4

=item command_options_factory

A C<command_options_factory> to use instead of that which is supplied by
this footprintless instance.

=item command_runner

lib/Footprintless/Mixins.pm  view on Meta::CPAN


our @EXPORT_OK = qw(
    _clean
    _command_options
    _deployment
    _download
    _entity
    _extract_resource
    _is_local
    _local_template
    _overlay
    _push_resource_to_destination
    _push_to_destination
    _resource
    _run
    _run_or_die
    _service
    _sub_coordinate
    _sub_entity
    _verify_required_entities
);

lib/Footprintless/Mixins.pm  view on Meta::CPAN

    else {
        $is_local = _is_local( $self, 'hostname' );
        $to_dir = $is_local ? $destination_dir : temp_dir();
    }

    &$local_work($to_dir);

    _push_to_destination( $self, $to_dir, $destination_dir ) unless ($is_local);
}

sub _overlay {
    my ( $self, $sub_coordinate, $name ) = @_;
    my $overlay = $name ? $self->{$name} : undef;
    unless ($overlay) {
        $overlay = $self->{factory}->overlay( _sub_coordinate( $self, $sub_coordinate ) );
        $self->{$name} = $overlay if ($name);
    }
    return $overlay;
}

sub _push_resource_to_destination {
    my ( $self, $resource, $destination_dir, %options ) = @_;

    my $temp_dir = temp_dir();
    if ( $options{extract} ) {
        _extract_resource( $self, $resource, $temp_dir );
    }
    else {

lib/Footprintless/Mixins.pm  view on Meta::CPAN

=head2 _local_template(\&local_work, [%options])

Will check if the entity at C<$self-E<gt>{coordinate}> has a C<hostname> that
is local, and if not, it will create a temp directory, call C<&local_work> 
with that directory, and call 
L<_push_to_destination/_push_to_destination($source, $destination, [%options])>
with the temp directory as the source, and C<to_dir> as the destination when 
complete.  If C<hostname> is I<not> local, then C<&local_work> is called
with C<to_dir>.

=head2 _overlay($sub_coordinate, [$name])

Returns a C<overlay> from the factory.  If C<$name> is supplied, the
C<overlay> will be a singleton stored at C<$self->{$name}>.

=head2 _push_resource_to_destination($source, $destination, [%options])

Pushes C<$source> to C<$destination>.  Supported options are:

=over 4

=item extract

If truthy, then the resource will be extracted using 

lib/Footprintless/Overlay.pm  view on Meta::CPAN

use strict;
use warnings;

package Footprintless::Overlay;
$Footprintless::Overlay::VERSION = '1.29';
# ABSTRACT: An overlay manager
# PODNAME: Footprintless::Overlay

use parent qw(Footprintless::MixableBase);

use Carp;
use Footprintless::Mixins qw (
    _clean
    _command_options
    _entity
    _extract_resource

lib/Footprintless/Overlay.pm  view on Meta::CPAN

        $self->_local_with_dirs_template(
            sub {
                $self->_initialize(@_);
            }
        );
    }
}

sub _initialize {
    my ( $self, $base_dir, $template_dir, $to_dir ) = @_;
    $self->_overlay($base_dir)->overlay(
        $template_dir,
        resolver => $self->_dot_footprintless_resolver(),
        to       => $to_dir
    );
}

sub _local_with_dirs_template {
    my ( $self, $local_work ) = @_;
    $self->_local_template(
        sub {
            $self->_dirs_template( $_[0], $local_work );
        }
    );
}

sub _overlay {
    my ( $self, $base_dir ) = @_;

    my @overlay_opts = ();
    my $key          = $self->_sub_entity('key');
    push( @overlay_opts, key => $key ) if ($key);

    return Template::Overlay->new( $base_dir, $self->_resolver(), @overlay_opts );
}

sub _resolver {
    my ($self) = @_;

    my @resolver_opts = ();
    my $os            = $self->_sub_entity('os');
    push( @resolver_opts, os => $os ) if ($os);

    my $resolver_coordinate = $self->_sub_entity('resolver_coordinate');
    my $resolver_spec =
          $resolver_coordinate
        ? $self->_entity($resolver_coordinate)
        : $self->_entity();

    my $resolver;
    my $resolver_factory = $self->_entity('footprintless.overlay.resolver_factory');
    if ($resolver_factory) {
        $logger->tracef( "using resolver_factory: %s", $resolver_factory );
        $resolver =
            dynamic_module_new($resolver_factory)->new_resolver( $resolver_spec, @resolver_opts );
    }
    else {
        $resolver = Template::Resolver->new( $resolver_spec, @resolver_opts );
    }
    return $resolver;
}

lib/Footprintless/Overlay.pm  view on Meta::CPAN

            sub {
                $self->_update(@_);
            }
        );
    }
}

sub _update {
    my ( $self, $base_dir, $template_dir, $to_dir ) = @_;
    $logger->tracef( "update to=[%s], template=[%s]", $to_dir, $template_dir );
    $self->_overlay($to_dir)
        ->overlay( $template_dir, resolver => $self->_dot_footprintless_resolver() );
}

1;

__END__

=pod

=head1 NAME

Footprintless::Overlay - An overlay manager

=head1 VERSION

version 1.29

=head1 SYNOPSIS

    # Standard way of getting an overlay
    use Footprintless;
    my $overlay = Footprintless->new()->overlay('overlay');

    $overlay->clean();

    $overlay->initialize();

    $overlay->update();

=head1 DESCRIPTION

Overlays are a combination of a directory of static files and a directory 
of templated files that will be merged to an output directory.  This
is implemented in L<Template::Overlay>.  

Additionally, any folder under the C<template_dir> can contain a 
C<.footprintless> file containing a C<clean> and/or C<resources> entities:

lib/Footprintless/Overlay.pm  view on Meta::CPAN

        }
    };

The C<clean> entity is an arrayref containing a list of paths to clean out.
These paths will be added to the path of the directory containing the
C<.footprintless> file.  The C<resources> entity is a list of resources to
download into the same directory as the C<.footprintless> file.

=head1 ENTITIES

A simple overlay: 

    overlay => {
        base_dir => "/home/me/foo/base",
        clean => [
            "/opt/tomcat/"
        ],
        hostname => 'localhost',
        key => 'T',
        os => 'linux',
        template_dir => "/home/me/foo/template",
        to_dir => '/opt/foo/tomcat'
    }

A more complex example:

    foo => {
        hostname => 'test.pastdev.com',
        overlay => {
            'Config::Entities::inherit' => ['hostname', 'sudo_username'],
            base_dir => '/home/me/foo/base',
            clean => [
                '/opt/foo/tomcat/'
            ],
            key => 'T',
            os => 'linux',
            resolver_coordinate => 'foo',
            template_dir => '/home/me/foo/template',
            to_dir => '/opt/foo/tomcat'

lib/Footprintless/Overlay.pm  view on Meta::CPAN

            },
            trust_store => {
                'Config::Entities::inherit' => ['hostname', 'sudo_username'],
                file => '/opt/foo/tomcat/certs/truststore.jks',
                include_java_home_cacerts => 1,
                password => $properties->{'foo.tomcat.trust_store.password'},
            }
        }
    }

An overlay can obtain base/template content from a resource.  When
initialize or update are called, the resource will be downloaded (if not
already local) and extracted to a temp folder.  The C<base_dir> and
C<template_dir> paths will be appended to the extract temp folder:

    overlay => {
        base_dir => 'base',
        clean => [
            '/opt/tomcat/'
        ],
        hostname => 'localhost',
        key => 'T',
        os => 'linux',
        resource => 'com.pastdev:app-overlay:zip:package:1.0.0',
        template_dir => 'template',
        to_dir => '/opt/foo/tomcat'
    }

An overlay can have multiple template folders.  If it does, they will
be processed in the order they are listed:

    overlay => {
        base_dir => 'base',
        clean => [
            '/opt/tomcat/'
        ],
        hostname => 'localhost',
        key => 'T',
        os => 'linux',
        template_dir => [
            'first/template_dir',
            'second/template_dir',
        ],
        to_dir => '/opt/foo/tomcat'
    }

=head1 CONSTRUCTORS

=head2 new($entity, $coordinate)

Constructs a new overlay configured by C<$entities> at C<$coordinate>.  

=head1 METHODS

=head2 clean()

Cleans the overlay.  Each path in the C<clean> entity, will be removed 
from the destination.  If the path ends in a C</>, then after being 
removed, the directory will be recreated.

=head2 initialize()

Will call C<clean>, then C<overlay> on an instance of L<Template::Overlay>
configured to this entity.  

=head2 update()

Will overlay I<ONLY> the templated files.  It will not C<clean>, nor copy 
any files from C<base_dir> like C<initialize> does.

=head1 AUTHOR

Lucas Theisen <lucastheisen@pastdev.com>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2016 by Lucas Theisen.

lib/Footprintless/Overlay.pm  view on Meta::CPAN

L<Template::Resolver|Template::Resolver>

=back

=head1 CONFIGURATION

This module can optionally be configured to use a customized resolver.  To
do so, configure a resolver factory in your entities:

    footprintless => {
        overlay => {
            resolver_factory => 'My::ResolverFactory'
        }
    }

The resolver factory must have a C<new_resolver> method that takes a spec and
a list of options and returns a C<Template::Resolver>, for example:

    sub new_resolver {
        my ($self, $resolver_spec, %resolver_opts) = @_;
        return Template::Resolver->new(

t/Footprintless_App_Command_config.t  view on Meta::CPAN

$ENV{FPL_CONFIG_PROPS} =
      File::Spec->catfile( $test_dir, 'config', 'properties.pl' )
    . ( ( $^O eq 'MSWin32' ) ? ';' : ':' )
    . File::Spec->catfile( $test_dir, 'config', 'environment.pl' );

is( test_app( 'Footprintless::App' => [ 'config', 'dev.foo.site', '--format', 'dumper0' ] )
        ->stdout(),
    "\$VAR1 = '';",
    'dev.foo.site empty string'
);
is( test_app( 'Footprintless::App' => [ 'config', 'dev.foo.overlay.os', '--format', 'dumper0' ] )
        ->stdout(),
    "\$VAR1 = 'linux';",
    'dev.foo.overlay.os = linux'
);
is( test_app( 'Footprintless::App' => [ 'config', 'dev.foo.logs', '--format', 'json2' ] )
        ->stdout(),
    '{"catalina":"/opt/pastdev/foo-tomcat/logs/catalina.out"}',
    'json dev.piab = {...}'
);

t/Footprintless_App_Command_deploy.t  view on Meta::CPAN

sub footprintless {
    my ( $temp_dir, $root_dir ) = @_;

    my $temp_config_dir = File::Spec->catdir( $temp_dir, 'config' );
    make_path($temp_config_dir);
    my $environment_dot_pl = File::Spec->catdir( $temp_config_dir, 'environment.pl' );
    spurt( <<"    EPL", $environment_dot_pl );
    return {
        'dev.foo.deployment.resources.dir' => '$test_dir/data/resources',
        'dev.foo.hostname' => 'localhost',
        'dev.foo.overlay.dir' => '$test_dir/data',
        'dev.foo.sudo_username' => undef,
        'dev.os' => '$^O',
        'dev.root.dir' => '$root_dir',
    }
    EPL

    # Get the current entities
    $ENV{FPL_CONFIG_DIRS} = File::Spec->catdir( $test_dir, 'config', 'entities' );
    $ENV{FPL_CONFIG_PROPS} =
          File::Spec->catfile( $test_dir, 'config', 'properties.pl' )

t/Footprintless_App_Command_overlay.t  view on Meta::CPAN

sub footprintless {
    my ( $temp_dir, $root_dir ) = @_;

    my $temp_config_dir = File::Spec->catdir( $temp_dir, 'config' );
    make_path($temp_config_dir);
    my $environment_dot_pl = File::Spec->catdir( $temp_config_dir, 'environment.pl' );
    spurt( <<"    EPL", $environment_dot_pl );
    return {
        'dev.foo.deployment.resources.dir' => '$test_dir/data/resources',
        'dev.foo.hostname' => 'localhost',
        'dev.foo.overlay.dir' => '$test_dir/data',
        'dev.foo.sudo_username' => undef,
        'dev.os' => '$^O',
        'dev.root.dir' => '$root_dir',
    }
    EPL

    # Get the current entities
    $ENV{FPL_CONFIG_DIRS} = File::Spec->catdir( $test_dir, 'config', 'entities' );
    $ENV{FPL_CONFIG_PROPS} =
          File::Spec->catfile( $test_dir, 'config', 'properties.pl' )
        . ( ( $^O eq 'MSWin32' ) ? ';' : ':' )
        . $environment_dot_pl;

    return Footprintless->new();
}

sub match {
    my ( $file, $footprintless, $coordinate, $action ) = @_;

    my $overlay = $footprintless->entities()->get_entity($coordinate);
    my $got_file = File::Spec->catdir( File::Spec->catdir( $overlay->{to_dir}, $file ) );
    ok( -f $got_file, "$action: $file is file" );

    my $original_content;
    my $original_file =
        File::Spec->catdir( File::Spec->catdir( $overlay->{template_dir}, $file ) );
    if ( -f $original_file ) {
        my @resolver_opts = ();
        if ( $overlay->{os} ) {
            push( @resolver_opts, os => $overlay->{os} );
        }
        my $resolver_spec =
              $overlay->{resolver_coordinate}
            ? $footprintless->entities()->get_entity( $overlay->{resolver_coordinate} )
            : $overlay;

        $original_content = Template::Resolver->new( $resolver_spec, @resolver_opts )->resolve(
            content => slurp($original_file),
            key     => $overlay->{key}
        );
    }
    else {
        $original_content = slurp( File::Spec->catdir( $overlay->{base_dir}, $file ) );
    }

    $logger->debugf( 'checking %s', $got_file );
    is( slurp($got_file), $original_content, "$action: $file matches expected" );
}

sub test_overlay {
    my ( $coordinate, $action, %options ) = @_;

    my $temp_dir = File::Temp->newdir();
    my $overlay_dir = File::Spec->catdir( $temp_dir, 'overlay' );
    make_path($overlay_dir);

    my $footprintless = footprintless( $temp_dir, $overlay_dir );
    my $overlay = $footprintless->entities()->get_entity($coordinate);

    unless ( $overlay->{base_dir} eq File::Spec->catfile( $test_dir, 'data', 'base' )
        && $overlay->{template_dir} eq File::Spec->catfile( $test_dir, 'data', 'template' )
        && $overlay->{to_dir} =~ /^$overlay_dir/ )
    {
        ;
        $logger->errorf(
            "%s=[%s]\n%s=[%s]\n%s starts with [%s]", $overlay->{base_dir},
            File::Spec->catfile( $test_dir, 'data', 'base' ),     $overlay->{template_dir},
            File::Spec->catfile( $test_dir, 'data', 'template' ), $overlay->{to_dir},
            $overlay_dir
        );
        BAIL_OUT('environment configuration broken, could be dangerous to proceed...');
    }
    $logger->debug('environment looks good, proceed...');

    if ( $logger->is_trace ) {
        $logger->tracef( 'overlay: %s', Data::Dumper->new( [$overlay] )->Indent(1)->Dump() );
    }

    Footprintless::App::clear_pretend_self();
    my $result = test_app(
        'Footprintless::App' => [
            'overlay', $coordinate,
            $action, ( $options{command_args} ? @{ $options{command_args} } : () )
        ]
    );
    is( $result->exit_code(), 0, "overlay completed succesfully" );
    if ( $logger->is_debug() ) {
        $logger->debugf(
            "exit_code=[%s],error=[%s]\n----- STDOUT ----\n%s\n---- STDERR ----\n%s\n---- END ----",
            $result->exit_code(), $result->error(), $result->stdout(), $result->stderr()
        );
    }

    &{ $options{validator} }($footprintless) if ( $options{validator} );
}

my $coordinate = 'dev.foo.overlay';
test_overlay(
    $coordinate,
    'update',
    validator => sub {
        my ($footprintless) = @_;
        match( 'bin/catalina.sh',         $footprintless, $coordinate, 'update' );
        match( 'bin/setenv.sh',           $footprintless, $coordinate, 'update' );
        match( 'conf/jndi-resources.xml', $footprintless, $coordinate, 'update' );
        match( 'conf/server.xml',         $footprintless, $coordinate, 'update' );
    }
);

test_overlay(
    $coordinate,
    'initialize',
    validator => sub {
        my ($footprintless) = @_;

        match( 'bin/catalina.sh',          $footprintless, $coordinate, 'initialize' );
        match( 'bin/setenv.sh',            $footprintless, $coordinate, 'initialize' );
        match( 'conf/jndi-resources.xml',  $footprintless, $coordinate, 'initialize' );
        match( 'conf/server.xml',          $footprintless, $coordinate, 'initialize' );
        match( 'conf/catalina.properties', $footprintless, $coordinate, 'initialize' );

t/Footprintless_Overlay.t  view on Meta::CPAN

    my $template_dir = File::Spec->catdir( $temp_dir, 'template' );
    make_path( $base_dir, $to_dir, $template_dir );

    return $temp_dir, $base_dir, $to_dir, $template_dir;
}

{
    $logger->info('Verify initialize');
    my ( $temp_dir, $base_dir, $to_dir, $template_dir ) = temp_dirs();
    my $hostname = 'localhost';
    my $overlay  = Footprintless::Overlay->new(
        factory(
            {   system => {
                    hostname => $hostname,
                    app      => {
                        'Config::Entities::inherit' => ['hostname'],
                        overlay                     => {
                            'Config::Entities::inherit' => [ 'hostname', 'sudo_username' ],
                            base_dir                    => $base_dir,
                            clean                       => ["$to_dir/"],
                            key                         => 'T',
                            os                          => $^O,
                            resolver_coordinate         => 'system',
                            template_dir                => $template_dir,
                            to_dir                      => $to_dir
                        }
                    }
                }
            }
        ),
        'system.app.overlay'
    );
    ok( $overlay, 'overlay constructed' );

    my $name          = 'foo';
    my $template_file = File::Spec->catfile( $template_dir, $name );
    my $to_file       = File::Spec->catfile( $to_dir, $name );
    spurt( 'hostname=[${T{app.hostname}}]', $template_file );
    my $base_template_file = File::Spec->catfile( $base_dir, $name );
    spurt( 'i should be overlayed', $base_template_file );
    my $base_name = 'bar';
    my $base_file = File::Spec->catfile( $base_dir, $base_name );
    spurt( 'bar', $base_file );
    $overlay->initialize();
    is( slurp($to_file), "hostname=[$hostname]", 'initialize template' );
    is( slurp($base_file), "bar", 'initialize base' );
}

{
    $logger->info('Verify update');
    my ( $temp_dir, $base_dir, $to_dir, $template_dir ) = temp_dirs();
    my $hostname = 'localhost';
    my $overlay  = Footprintless::Overlay->new(
        factory(
            {   system => {
                    hostname => $hostname,
                    app      => {
                        'Config::Entities::inherit' => ['hostname'],
                        overlay                     => {
                            'Config::Entities::inherit' => [ 'hostname', 'sudo_username' ],
                            base_dir                    => $base_dir,
                            clean                       => ["$to_dir/"],
                            key                         => 'T',
                            os                          => $^O,
                            resolver_coordinate         => 'system',
                            template_dir                => $template_dir,
                            to_dir                      => $to_dir
                        }
                    }
                }
            }
        ),
        'system.app.overlay'
    );
    ok( $overlay, 'overlay constructed' );

    my $name          = 'foo';
    my $template_file = File::Spec->catfile( $template_dir, $name );
    my $to_file       = File::Spec->catfile( $to_dir, $name );
    spurt( 'hostname=[${T{app.hostname}}]', $template_file );
    $overlay->update();
    is( slurp($to_file), "hostname=[$hostname]", 'update' );
}

{
    $logger->info('Verify clean');
    my ( $temp_dir, $base_dir, $to_dir, $template_dir ) = temp_dirs();
    my $overlay = Footprintless::Overlay->new(
        factory(
            {   overlay => {
                    hostname            => 'localhost',
                    base_dir            => $base_dir,
                    clean               => ["$to_dir/"],
                    key                 => 'T',
                    os                  => $^O,
                    resolver_coordinate => 'system',
                    template_dir        => $template_dir,
                    to_dir              => $to_dir
                }
            }
        ),
        'overlay'
    );
    my $to_file = File::Spec->catfile( $to_dir, 'bar' );
    spurt( 'foo', $to_file );
    ok( -f $to_file, 'clean test to_file created' );
    $overlay->clean();
    ok( !-e $to_file, 'clean test' );
}

{
    $logger->info('Verify resolver factory');
    my ( $temp_dir, $base_dir, $to_dir, $template_dir ) = temp_dirs();
    my $hostname = 'localhost';
    my $overlay  = Footprintless::Overlay->new(
        factory(
            {   system => {
                    hostname => $hostname,
                    app      => {
                        'Config::Entities::inherit' => ['hostname'],
                        overlay                     => {
                            'Config::Entities::inherit' => [ 'hostname', 'sudo_username' ],
                            base_dir                    => $base_dir,
                            clean                       => ["$to_dir/"],
                            key                         => 'T',
                            os                          => $^O,
                            resolver_coordinate         => 'system',
                            template_dir                => $template_dir,
                            to_dir                      => $to_dir
                        },
                        web => {
                            'Config::Entities::inherit' => ['hostname'],
                            https                       => 1,
                            port                        => 8443,
                            context_path                => '/foo'
                        },
                    },
                },
                footprintless =>
                    { overlay => { resolver_factory => 'Footprintless::WebUrlResolverFactory' } }
            }
        ),
        'system.app.overlay'
    );
    ok( $overlay, 'overlay constructed with resolver factory' );

    my $name          = 'foo';
    my $template_file = File::Spec->catfile( $template_dir, $name );
    my $to_file       = File::Spec->catfile( $to_dir, $name );
    spurt( 'url=[${T_web_url{app.web}}]', $template_file );
    $overlay->update();
    is( slurp($to_file), "url=[https://$hostname:8443/foo]", 'update with resolver factory' );
}

{
    $logger->info('Verify resolve .footprintless placeholders');
    my ( $temp_dir, $base_dir, $to_dir, $template_dir ) = temp_dirs();
    my $hostname = 'localhost';

    my @downloads = ();
    {

        package Mock::ResourceManager;

        sub download {
            my ( $self, @args ) = @_;
            push( @downloads, \@args );
        }
    }

    my $overlay = Footprintless::Overlay->new(
        factory(
            {   system => {
                    hostname => $hostname,
                    app      => {
                        'Config::Entities::inherit' => ['hostname'],
                        overlay                     => {
                            'Config::Entities::inherit' => [ 'hostname', 'sudo_username' ],
                            base_dir                    => $base_dir,
                            clean                       => ["$to_dir/"],
                            key                         => 'T',
                            os                          => $^O,
                            resolver_coordinate         => 'system',
                            template_dir                => $template_dir,
                            to_dir                      => $to_dir
                        },
                        web => {
                            'Config::Entities::inherit' => ['hostname'],
                            https                       => 1,
                            port                        => 8443,
                            context_path                => '/foo'
                        },
                    },
                }
            },
            resource_manager => bless( {}, 'Mock::ResourceManager' )
        ),
        'system.app.overlay'
    );
    ok( $overlay, 'overlay constructed with mock resource manager' );

    my $dot_footprintless = File::Spec->catfile( $template_dir, '.footprintless' );
    spurt( 'return {clean => ["./"], resources => {foo => "bar"}};', $dot_footprintless );
    $overlay->initialize();
    is( @downloads == 1 && $downloads[0][0], 'bar', 'bar was downloaded by initialize' );

    my $baz = File::Spec->catfile( $to_dir, 'baz' );
    spurt( 'testing...', $baz );
    ok( -f $baz, 'baz is ready to be cleaned' );
    $overlay->update();
    ok( !-e $baz, 'clean worked' );
    is( @downloads == 2 && $downloads[1][0], 'bar', 'bar was downloaded by update' );
}

{
    $logger->info('Verify alternate to_dir');
    my ( $temp_dir, $base_dir, $to_dir, $template_dir ) = temp_dirs();
    my $alternate_to_dir = File::Spec->catdir( $temp_dir, 'alternate' );
    make_path($alternate_to_dir);

    my $hostname = 'localhost';
    my $overlay  = Footprintless::Overlay->new(
        factory(
            {   system => {
                    hostname => $hostname,
                    app      => {
                        'Config::Entities::inherit' => ['hostname'],
                        overlay                     => {
                            'Config::Entities::inherit' => [ 'hostname', 'sudo_username' ],
                            base_dir                    => $base_dir,
                            clean                       => ["$to_dir/"],
                            key                         => 'T',
                            os                          => $^O,
                            resolver_coordinate         => 'system',
                            template_dir                => $template_dir,
                            to_dir                      => $to_dir
                        }
                    }
                }
            }
        ),
        'system.app.overlay'
    );
    ok( $overlay, 'overlay constructed' );

    my $name              = 'foo';
    my $template_file     = File::Spec->catfile( $template_dir, $name );
    my $to_file           = File::Spec->catfile( $to_dir, $name );
    my $alternate_to_file = File::Spec->catfile( $alternate_to_dir, $name );
    spurt( 'hostname=[${T{app.hostname}}]', $template_file );
    my $base_template_file = File::Spec->catfile( $base_dir, $name );
    spurt( 'i should be overlayed', $base_template_file );
    my $base_name = 'bar';
    my $base_file = File::Spec->catfile( $base_dir, $base_name );
    spurt( 'bar', $base_file );

    $overlay->initialize( to_dir => $alternate_to_dir );
    ok( !-e $to_file, 'configured initialize template does not exist' );
    is( slurp($alternate_to_file), "hostname=[$hostname]", 'alternate initialize template' );
    is( slurp($base_file), "bar", 'configured initialize base' );

    unlink($alternate_to_file);
    ok( !-e $alternate_to_file, 'alternate_to_file deleted' );

    $overlay->update( to_dir => $alternate_to_dir );
    ok( !-e $to_file, 'configured update template does not exist' );
    is( slurp($alternate_to_file), "hostname=[$hostname]", 'alternate update' );
}

SKIP: {
    $logger->info('Verify non-local');
    my $command_runner = default_command_runner();
    eval {
        $command_runner->run_or_die( 'ssh -q -o "StrictHostKeyChecking=yes" localhost echo hello',
            { timeout => 2 } );
    };
    if ($@) {
        skip( "cannot ssh to localhost: $@", 3 );
    }

    my ( $temp_dir, $base_dir, $to_dir, $template_dir ) = temp_dirs();
    my $hostname = 'localhost';
    my $overlay  = Footprintless::Overlay->new(
        factory(
            {   system => {
                    hostname => $hostname,
                    app      => {
                        'Config::Entities::inherit' => ['hostname'],
                        overlay                     => {
                            'Config::Entities::inherit' => [ 'hostname', 'sudo_username' ],
                            base_dir                    => $base_dir,
                            clean                       => ["$to_dir/"],
                            key                         => 'T',
                            os                          => $^O,
                            resolver_coordinate         => 'system',
                            template_dir                => $template_dir,
                            to_dir                      => $to_dir
                        }
                    }
                }
            }
        ),
        'system.app.overlay',
        localhost => Footprintless::Localhost->new( none => 1 )
    );
    ok( $overlay, 'overlay constructed' );

    my $name          = 'foo';
    my $template_file = File::Spec->catfile( $template_dir, $name );
    my $to_file       = File::Spec->catfile( $to_dir, $name );
    spurt( 'hostname=[${T{app.hostname}}]', $template_file );
    my $base_template_file = File::Spec->catfile( $base_dir, $name );
    spurt( 'i should be overlayed', $base_template_file );
    my $base_name = 'bar';
    my $base_file = File::Spec->catfile( $base_dir, $base_name );
    spurt( 'bar', $base_file );
    $overlay->initialize();
    is( slurp($to_file), "hostname=[$hostname]", 'non-local initialize template' );
    is( slurp($base_file), "bar", 'non-local initialize base' );
}

SKIP: {
    $logger->info('Verify resource overlay');
    my ( $temp_dir, $base_dir, $to_dir, $template_dir ) = temp_dirs();
    my $hostname = 'localhost';

    my $overlay = Footprintless::Overlay->new(
        factory(
            {   system => {
                    hostname => $hostname,
                    app      => {
                        'Config::Entities::inherit' => ['hostname'],
                        overlay                     => {
                            'Config::Entities::inherit' => [ 'hostname', 'sudo_username' ],
                            base_dir                    => 'base',
                            clean                       => ["$to_dir/"],
                            key                         => 'T',
                            os                          => $^O,
                            resource                    => File::Spec->catfile(
                                $test_dir, 'data', 'resources', 'overlay.zip'
                            ),
                            resolver_coordinate => 'system',
                            template_dir        => 'template/first',
                            to_dir              => $to_dir
                        },
                    },
                }
            }
        ),
        'system.app.overlay'
    );
    ok( $overlay, 'resource overlay constructed' );

    $overlay->initialize();
    my $foo_file = File::Spec->catfile( $to_dir, 'foo.txt' );
    my $bar_file = File::Spec->catfile( $to_dir, 'bar.txt' );
    my $baz_file = File::Spec->catfile( $to_dir, 'baz.txt' );
    ok( -f $foo_file, 'resource overlay initialize foo exists' );
    is( slurp($foo_file), "hostname=[foo]\n", 'resource overlay initialize foo matches' );
    ok( -f $bar_file, 'resource overlay initialize bar exists' );
    is( slurp($bar_file),
        "first.bar.hostname=[$hostname]\n",
        'resource overlay initialize bar matches'
    );
    ok( -f $baz_file, 'resource overlay initialize baz exists' );
    is( slurp($baz_file),
        "first.baz.hostname=[$hostname]\n",
        'resource overlay initialize baz matches'
    );

    $overlay->clean();
    ok( !( -e $foo_file ), 'resource overlay clean foo' );
    ok( !( -e $bar_file ), 'resource overlay clean bar' );
    ok( !( -e $baz_file ), 'resource overlay clean baz' );

    $overlay->update();
    ok( !( -e $foo_file ), 'resource overlay update foo missing' );
    ok( -f $bar_file,      'resource overlay update bar exists' );
    is( slurp($bar_file),
        "first.bar.hostname=[$hostname]\n",
        'resource overlay update bar matches'
    );
    ok( -f $baz_file, 'resource overlay update baz exists' );
    is( slurp($baz_file),
        "first.baz.hostname=[$hostname]\n",
        'resource overlay update baz matches'
    );
}

SKIP: {
    $logger->info('Verify multi-template resource overlay');
    my ( $temp_dir, $base_dir, $to_dir, $template_dir ) = temp_dirs();
    my $hostname = 'localhost';

    my $overlay = Footprintless::Overlay->new(
        factory(
            {   system => {
                    hostname => $hostname,
                    app      => {
                        'Config::Entities::inherit' => ['hostname'],
                        overlay                     => {
                            'Config::Entities::inherit' => [ 'hostname', 'sudo_username' ],
                            base_dir                    => 'base',
                            clean                       => ["$to_dir/"],
                            key                         => 'T',
                            os                          => $^O,
                            resource                    => File::Spec->catfile(
                                $test_dir, 'data', 'resources', 'overlay.zip'
                            ),
                            resolver_coordinate => 'system',
                            template_dir        => [ 'template/first', 'template/second', ],
                            to_dir              => $to_dir
                        },
                    },
                }
            }
        ),
        'system.app.overlay'
    );
    ok( $overlay, 'multi-template resource overlay constructed' );

    $overlay->initialize();
    my $foo_file = File::Spec->catfile( $to_dir, 'foo.txt' );
    my $bar_file = File::Spec->catfile( $to_dir, 'bar.txt' );
    my $baz_file = File::Spec->catfile( $to_dir, 'baz.txt' );
    ok( -f $foo_file, 'multi-template resource overlay initialize foo exists' );
    is( slurp($foo_file), "hostname=[foo]\n",
        'multi-template resource overlay initialize foo matches' );
    ok( -f $bar_file, 'multi-template resource overlay initialize bar exists' );
    is( slurp($bar_file),
        "second.bar.hostname=[$hostname]\n",
        'multi-template resource overlay initialize bar matches'
    );
    ok( -f $baz_file, 'multi-template resource overlay initialize baz exists' );
    is( slurp($baz_file),
        "first.baz.hostname=[$hostname]\n",
        'multi-template resource overlay initialize baz matches'
    );

    $overlay->clean();
    ok( !( -e $foo_file ), 'multi-template resource overlay clean foo' );
    ok( !( -e $bar_file ), 'multi-template resource overlay clean bar' );
    ok( !( -e $baz_file ), 'multi-template resource overlay clean baz' );

    $overlay->update();
    ok( !( -e $foo_file ), 'multi-template resource overlay update foo missing' );
    ok( -f $bar_file,      'multi-template resource overlay update bar exists' );
    is( slurp($bar_file),
        "second.bar.hostname=[$hostname]\n",
        'multi-template resource overlay update bar matches'
    );
    ok( -f $baz_file, 'multi-template resource overlay update baz exists' );
    is( slurp($baz_file),
        "first.baz.hostname=[$hostname]\n",
        'multi-template resource overlay update baz matches'
    );
}

t/config/entities/dev.pm  view on Meta::CPAN

                "$foo_tomcat_directory/webapps/",                 "$foo_tomcat_directory/work/",
            ],
            resources => {
                bar => "$properties{'dev.foo.deployment.resources.dir'}/bar.war",
                baz => "$properties{'dev.foo.deployment.resources.dir'}/baz.war"
            },
            to_dir => "$foo_tomcat_directory/webapps",
        },
        hostname => $properties->{'dev.foo.hostname'},
        logs     => { catalina => "$foo_tomcat_directory/logs/catalina.out", },
        overlay  => {
            'Config::Entities::inherit' => [ 'hostname', 'sudo_username' ],
            base_dir            => "$properties{'dev.foo.overlay.dir'}/base",
            clean               => [ "$foo_tomcat_directory/" ],
            key                 => 'T',
            os                  => $properties{'dev.os'},
            resolver_coordinate => $coord,
            template_dir        => "$properties{'dev.foo.overlay.dir'}/template",
            to_dir              => $foo_tomcat_directory
        },
        site          => '',
        sudo_username => $properties->{'dev.foo.sudo_username'},
        tomcat        => {
            'Config::Entities::inherit' => [ 'hostname', 'sudo_username' ],
            ajp           => { port => 8509 },
            catalina_base => $foo_tomcat_directory,
            http          => { port => 8580 },
            jmx_port      => 8587,

t/config/environment.pl  view on Meta::CPAN

use File::Spec;

my $data_dir = File::Spec->catdir( substr( __FILE__, 0, rindex( __FILE__, "config" ) ), 'data' );

return {
    'dev.foo.deployment.resources.dir' => "$data_dir/resources",
    'dev.foo.hostname'                 => 'app.pastdev.local',
    'dev.foo.overlay.dir'              => $data_dir,
    'dev.foo.sudo_username'            => 'foo',
    'dev.os'                           => 'linux',
    'dev.root.dir'                     => '/opt/pastdev',
    }

xt/author/00-compile.t  view on Meta::CPAN

    'Footprintless/App/Command/config.pm',
    'Footprintless/App/Command/deployment.pm',
    'Footprintless/App/Command/deployment/clean.pm',
    'Footprintless/App/Command/deployment/deploy.pm',
    'Footprintless/App/Command/log.pm',
    'Footprintless/App/Command/log/cat.pm',
    'Footprintless/App/Command/log/follow.pm',
    'Footprintless/App/Command/log/grep.pm',
    'Footprintless/App/Command/log/head.pm',
    'Footprintless/App/Command/log/tail.pm',
    'Footprintless/App/Command/overlay.pm',
    'Footprintless/App/Command/overlay/clean.pm',
    'Footprintless/App/Command/overlay/initialize.pm',
    'Footprintless/App/Command/overlay/update.pm',
    'Footprintless/App/Command/service.pm',
    'Footprintless/App/DocumentationUtil.pm',
    'Footprintless/App/UsageException.pm',
    'Footprintless/Command.pm',
    'Footprintless/CommandOptionsFactory.pm',
    'Footprintless/CommandRunner.pm',
    'Footprintless/CommandRunner/ExecutionException.pm',
    'Footprintless/CommandRunner/IPCRun.pm',
    'Footprintless/CommandRunner/IPCRun3.pm',
    'Footprintless/Deployment.pm',



( run in 1.317 second using v1.01-cache-2.11-cpan-49f99fa48dc )