NAME

Stepford::Runner - Takes a set of steps and figures out what order to run them in

VERSION

version 0.005000

SYNOPSIS

use Stepford::Runner;

my $runner = Stepford::Runner->new(
    step_namespaces => 'My::Step',
);

$runner->run(
    final_steps => [
        'My::Step::DeployCatDatabase',
        'My::Step::DeployDogDatabase',
    ],
    config => {...},
);

DESCRIPTION

This class takes a set of objects which do the Stepford::Role::Step role and determines what order they should be run so as to get to one or more final steps.

Steps which are up to date are skipped during the run, so no unnecessary work is done.

METHODS

This class provides the following methods:

Stepford::Runner->new(...)

This method returns a new runner object. It accepts the following arguments:

  • step_namespaces

    This argument is required.

    This can either be a string or an array reference of strings. Each string should contain a namespace which contains step classes.

    For example, if your steps are named My::Step::CreateFoo, My::Step::MergeFooAndBar, and My::Step::DeployMergedFooAndBar, the namespace you'd provide is 'My::Step'.

    The order of the step namespaces is relevant. If more than one step has a production of the same name, then the first step "wins". Stepford sorts step class names based on the order of the namespaces provided to the constructor, and then the full name of the class. You can take advantage of this feature to provide different steps in different environments (for example, for testing).

    The runner object assumes that every class it finds in a step namespace is a step. Specifically, if it finds a package that is a subclass of Moose::Object, then it throws an error if that package does not also consume the Stepford::Role::Step role.

    This means you can have utility packages and roles in a step namespace, but not Moose objects which aren't steps.

  • jobs

    This argument defaults to 1.

    The number of jobs to run at a time. By default, all steps are run sequentially. However, if you set this to a value greater than 1 then the runner will run steps in parallel, up to the value you set.

  • logger

    This argument is optional.

    This should be an object that provides debug, info, notice, warning, and error methods.

    This object will receive log messages from the runner and (possibly your steps).

    If this is not provided, Stepford will create a Log::Dispatch object with a single Log::Dispatch::Null output (which silently eats all the logging messages).

    Note that if you do provide a logger object of your own, Stepford will not load Log::Dispatch into memory.

$runner->run

When this method is called, the runner comes up with a plan of the steps needed to get to the requested final steps.

When this method is called, we check for circular dependencies among the steps and will throw a Stepford::Error exception if it finds one. We also check for unsatisfied dependencies for steps in the plan. Finally, we check to make sure that no step provides its own dependencies as productions.

For each step, the runner checks if it is up to date compared to its dependencies (as determined by the $step->last_run_time method. If the step is up to date, it is skipped, otherwise the runner calls $step->run on the step. You can avoid this check and force all steps to be executed with the force_step_execution parameter (documented below.)

Note that the step objects are always constructed, so you should avoid doing a lot of work in your constructor. Save that for the run method.

This method accepts the following parameters:

  • final_steps

    This argument is required.

    This can either be a string or an array reference of strings. Each string should be a step's class name. These classes must do the Stepford::Role::Step role.

    These are the final steps run when the $runner->run method is called.

  • config

    This is an optional hash reference. For each step constructed, the runner looks at the attributes that the step accepts. If they match any of the keys in this hash reference, the key/value pair from this hash reference will be passed to the step constructor. This matching is done based on attribute init_arg values.

    Note that values generated as productions from previous steps will override the corresponding key in the config hash reference.

  • force_step_execution

    This argument defaults to false.

    This controls if we should force all steps to be executed rather than checking which steps are up to date and do not need re-executing. Even with this set each step will only be executed once per run regardless of how many other steps depend on it during execution.

  • dry_run

    This argument defaults to none.

    When set to a valid output mode, this option makes Stepford calculate the steps that need to be executed, but stop before actually executing them. Instead, the step graph will be dumped to STDOUT using one of the following output methods available in Graph::Easy:

    • txt

    • ascii

    • boxart

    • svg

    • graphviz

    • graphml

    • vcg

    • gdl

    These will be mapped directly to the corresponding methods in that module.

$runner->step_namespaces

This method returns the step namespaces passed to the constructor as a list (not an arrayref).

$runner->logger

This method returns the logger used by the runner, either what you passed to the constructor or a default.

PARALLEL RUNS AND SERIALIZATION

When running steps in parallel, the results of a step (its productions) are sent from a child process to the parent by serializing them. This means that productions which can't be serialized (like a filehandle or DBI handle) will cause the runner to die when it tries to serialize their productions.

You can force a step class to be run in the same process as the runner itself by having that step consume the Stepford::Role::Step::Unserializable role. Note that the runner may still fork after a production has been generated, so the values returned for a production must be able to survive a fork, even if they cannot be serialized.

You can also work around this by changing the production entirely. For example, instead of passing a DBI handle you could pass a data structure with a DSN, username, password, and connection options.

SUPPORT

Bugs may be submitted through https://github.com/maxmind/Stepford/issues.

AUTHOR

Dave Rolsky <drolsky@maxmind.com>

COPYRIGHT AND LICENSE

This software is copyright (c) 2014 - 2018 by MaxMind, Inc.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.