DevCamps Documentation: Config

NAME

Camp::Config - module for determining operating environment and configuration settings

VERSION

3.05

DESCRIPTION

Camp::Config is a basic utility module that understands the camp layout of the Camp development environments as well as the layout of production environments for both Interchange and associated catalogs; it allows for easy manipulation of your Perl lib search paths (to include interchange/lib and interchange/custom/lib in your scripts without hardcoding paths), and also reads standard Interchange configuration files for variables at both the daemon and catalog level. In addition, it provides methods for accessing this information, such that Perl modules may safely rely on configuration variables and such without needing to rely on Interchange actually being present.

USAGE

One of the simpler, more obvious usages of Camp::Config is within test scripts. Test scripts need to run in any camp, as well as in production. This has traditionally caused problems for scripts that need to test modules within the Interchange standard and custom library paths, as there was no convenient way around hardcoding the use lib ... calls within the test script. Camp::Config, by knowing about the environment in which it is running, takes care of this for you.

An unfriendly, unportable hard-coded implementation, like this:

 #!/usr/local/bin/perl
 use lib "$ENV{HOME}/camp45/interchange/lib";
 use lib "$ENV{HOME}/camp45/interchange/custom/lib";
 use Test::More tests => 2010;
 use Vend::Util;
 use Camp::Catalogs::Foo;
 ...

can now be implemented in the following easy, portable fashion:

 #!/usr/local/bin/perl
 use Camp::Config use_libs => 1;
 use Test::More tests => 2010;
 use Vend::Util ();
 use Camp::Catalogs::Foo;
 ..

That replaced two lines of code with one, so it's not a huge savings. But it's not nearly as annoying as typing raw paths. Plus, this will work on any camp or on production without requiring a single alteration. That's the point, as it allows you to store the code verbatim in version control and use it in any environment without modification.

Note that the 'use_libs => 1' passed to the use call is necessary to tell Camp::Config to alter the library search paths at import. This also could be accomplished via:

 use Camp::Config;
 use Test::More tests => 2010;
 BEGIN {
     Camp::Config->include_paths;
 }
 use Vend::Util;
 use Camp::Catalogs::Foo;
 ...

In the above case, a BEGIN block would be necessary since use calls are checked at compile time; the subsequent use calls would fail if the include_paths() method wasn't called. This is obviously less user-friendly than simply using the module with the use_libs option, so just go with that option unless you have a really good reason.

That's really only the beginning of the benefits of using a module for determining this kind of thing. As stated earlier, Camp::Config parses the standard Interchange configuration files at the daemon/global level and the catalog level, and understands the Variable directive (including variable interpolation therein) along with the ParseVariables directive. As such, Perl modules to be used within Interchange (or outside of it, in fact) can use Camp::Config's interface to access this information and be confident of working both in IC and externally (for purposes of testing, or for use in external scripts, etc.). It also can follow include directive calls, meaning that all configuration files involved with the daemon and catalogs are seen.

For instance, we might want to use Module::Refresh to reload modules at a certain point in a process, but only if in camp mode:

 package My::Awesome::Package;
 use strict;
 use warnings;
 use Camp::Config;
 # don't even load the module if we're not in development.
 eval "use Module::Refresh"
     if Camp::Config->camp
 ;

 sub refresh {
     return unless Camp::Config->camp;
     return Module::Refresh->refresh;
 }

 sub do_awesome_stuff {
     my $self = shift;
     $self->refresh;
     ....here's where you implement your awesomeness...
 }

 Module::Refresh->new if Camp::Config->camp;
 ...

Or, perhaps we need to know information about the current catalog and some database-oriented variables.

 sub introspect {
     my $self = shift;
     $self->{catalog} = Camp::Config->catalog;
     $self->{db_user} = Camp::Config->smart_variable( 'SQLUSER' );
     $self->{db_dsn}  = Camp::Config->smart_variable( 'SQLDSN' );
     return;
 }

As these examples hopefully demonstrate, Camp::Config exposes all our Interchange-space configuration variables and such through a simple interface that can be used within and beyond Interchange, eliminating the need for duplication of configuration information and increasing the portability and maintainability of code.

ASSUMPTIONS ABOUT ENVIRONMENT

Camp::Config relies on a combination of environment variables (managed by the chcamp utility) and Interchange global variables to determine the particulars of the runtime environment in which it is invoked. Three pieces of information are determined from this:

File layout

File layouts are either 'per-user' (meaning the "camp" setup in which a particular camp lives in a directory immediately under the owner's home directory) or 'system-wide' (meaning the RPM-style IC install expected when the user is 'interch').

Application role

A given Interchange daemon expects to either be "frontside" (public-facing, without the in-house management apps activated) or "backside" (in-house facing, with all management and inventory functionality available).

Run environment

Indicates the basic purpose for which the Interchange is being run. Distinguishes between development (or camp), QA, staging, production.

Upon compiling, the module looks first to see if a per-user camp layout is active (based on how chcamp sets this) and, if so, verifies that the running user is the owner of that camp. If no camp is specified in the environment, Camp::Config checks to see if the running user is 'interch', meaning that we are operating with a system-wide file layout.

When determined to be in per-user mode (meaning within a camp, with a valid camp owner as the effective UID of the process), paths will be set relative to that camp:

  • base_path => "home/$username/camp$camp_number"
  • ic_path => base_path . '/interchange'

When the file layout is system-wide RPM-style, the paths are set up for an RPM-install Interchange:

  • base_path => '/var/lib/interchange'
  • ic_path => '/usr/lib/interchange'

Note that the base_path is where catalogs, scripts, etc. are expected to reside, and is not necessarily the "root" of the environment (though it effectively is in the case of camps); the ic_path is where the interchange code/configuration lives.

Library paths added by the use_libs option at import, or by calls to include_paths(), are relative to ic_root and are:

  • lib
  • custom/lib

(Added in the above order, meaning the latter path takes precedence.)

The environment determination is done at compile time, meaning that any environment problems/inconsistencies will prevent compilation of code that uses this module. This means that camps require a "chcamp" call up front to set the active camp, or that production environments must be launched with no camp set by the interch user.

In addition to environment stuff, configuration files are also processed at compile time, meaning that the IC-level file(s) are processed first in order to determine IC-level variables and identify known catalogs, followed by processing of catalog-level files per-catalog.

Consistent with how Interchange itself operates, Camp::Config begins config-file parsing at interchange.cfg (in your ic_path()). Per catalog found, catalog config parsing begins at the catalog.cfg file (in the catalog_path()). Catalog parsing begins only after all Interchange-level parsing is complete. At each level, include directives are followed on-demand (meaning that the file (or files) included is parsed before proceeding with the rest of the outer file that contained the include).

The entire process will die if any IC-level files are missing; however, warnings will be issued for any missing catalog-level files, without throwing exceptions.

Within any of the configuration files parsed, Camp::Config will look for Variable and Include declarations; for IC-level files, Catalog directives are parsed as well; for catalog-level files, the <ParseVariables> directive is also supported. The behaviors of Variable and ParseVariables are designed to be consistent with Interchange itself, to ensure that variables have the same value/meaning within Camp::Config and the various IC-space repositories. Block-style declarations can be used with ParseVariables and this module will understand it.

The Interchange-level configuration file(s) must specify meaningful values for APPLICATION_ROLE and RUN_ENVIRONMENT. After parsing the IC-level files for global variables, the settings in these variables will be validated and initialization will fail if the settings are invalid. Therefore, like chcamp, any process relying on Camp::Config for operation will have compilation errors if the variables aren't properly prepared. The valid values for each:

APPLICATION_ROLE

"backside", "frontside"

RUN_ENVIRONMENT

"camp", "production", "qa", "staging"

HERE DOCS

Camp::Config is heredoc-aware, meaning that (like Interchange itself) it handles heredocs as they come up within a config file, even if it doesn't understand the directive to which the heredoc applies. In this way, Camp::Config knows to treat content within a heredoc as a value associated with a directive, rather than processing that content for directives. Similarly, heredocs can be used with the directives that Camp::Config does understand.

Camp::Config will throw an exception if any file parsed contains an unterminated heredoc.

CONDITIONS (IFDEF, IFNDEF)

Camp::Config understands #ifdef and #ifndef statements, and treats them exactly as does Interchange itself. For clarity's sake, these declarations take the following form:

 #if(n)def VARIABLE_NAME [ perl_expression ]
 ...other directives...
 #endif

The VARIABLE_NAME is expected to correspond to a variable of the same scope as the level of parsing (meaning that for global files it would correspond to a global variable and for catalog files it would correspond to a catalog variable). At the catalog level, the VARIABLE_NAME may be prefixed by an at symbol ("@") to indicate that the variable is global rather than catalog-level.

If the optional perl_expression is provided, it will be evaled with the value of of VARIABLE_NAME, such variable FOO with value "bar" in the following directive:

 #ifdef FOO eq 'pooh'

would eval as

 'bar' eq 'pooh'

and thus be false.

If the expression is left out, the check is based on the perly truth of the variable named; when provided, the check is based on the perly truth of the eval operation. The #ifdef directive will be true if the result is true; the #ifndef directive will be true if the result is false. Configuration lines within a true #if(n)def block will be processed, and within a false block ignored. The blocks must be terminated by #endif.

Camp::Config will throw an exception if any file parsed contains an unterminated #ifdef block.

METHODS

Though this module does not currently provide objects, all methods are object-style, meaning they should be invoked with "$package->method()" syntax; no subs are exported. This was done to keep it consistent with the vast majority of modules being implemented for MVC2.0.

per_user_file_layout()

Returns a boolean value with truth indicating that the Interchange uses a per-user "camp" file layout, and false indicating an RPM-style system-wide file layout.

run_environment()

Returns 'camp', 'production', 'qa', or 'staging' depending on the RUN_ENVIRONMENT Interchange global variable value.

camp()

Returns true if the run_environment() is 'camp'.

production()

Returns true if the run_environment() is 'production'.

qa()

Returns true if the run_environment() is 'qa'.

staging()

Returns true if the run_environment() is 'staging'.

application_role()

Returns 'frontside' or 'backside' depending on the APPLICATION_ROLE Interchange global variable value.

backside()

Returns true if the application_role() is 'backside'.

frontside()

Returns true if the application_role() is 'frontside'.

user()

Returns an User::pwent object representing the password-file information about the owner of the current camp/environment.

camp_number()

Returns the current development camp number when in development mode; returns undef in production mode.

ic_path()

Returns the path of the environment's interchange install.

base_path()

Returns the "base path" for the environment, where all catalogs, common files, scripts, etc. are expected to live.

catalog()

Returns the name of the active catalog, based on internal values in the Interchange global space; returns undef if no catalog is active (meaning that you're probably not running in Interchange, or running in Interchange before catalogs are initialized).

catalog_path( $catalog )

Returns the catalog root path of $catalog, throwing an error if $catalog is not known to Camp::Config.

variable( $catalog, [ $variable ] )

In scalar context: Returns the value (or undef if nonexistent) of $variable within catalog $catalog; use undef for $catalog if the IC-level variable is desired. Note that variable() does not cascade values from IC level down to catalog level; the value returned will be specific to $catalog only.

In list context, with no $variable specified: returns a name/value pair list of all variables known within $catalog. This is literally a name/value pair list; it cannot be used to change the values within the underlying hash.

smart_variable( $variable, [ $catalog ] )

Similar to the scalar-context version of variable(), except that this performs a cascade such that if the requested $variable does not exist within the catalog, the value in the IC-level space will be returned. This makes it approximately equivalent to variable identifiers of the style @_IDENTIFIER_@ in ITL.

The $catalog argument is optional; when not specified, the default behavior is to use the value of the catalog() method. If $catalog is given undef, or if left unspecified and catalog() returns undef, then only the IC variables will be consulted.

databasedefault( $catalog, $name )

In scalar context: Returns the value (or undef if nonexistent) of $name in catalog $catalog's DatabaseDefault configuration.

In list context, with no $name specified: returns a name/value pair list of all DatabaseDefault configuration known within $catalog. This is literally a name/value pair list; it cannot be used to change the values within the underlying hash.

known_catalogs( [ $name ] )

If optional $name is specified, returns a boolean indicating whether that catalog is known to Camp::Config.

If $name is not provided, returns an alphabetically-sorted list of known catalog names in list context, or an arrayref of the same in scalar context.

include_paths()

Modifies the @INC search paths to include the relevant Interchange-relative libary paths, based on ic_path for the current environment.

import( %options )

Like the import() sub of most any exporter, this isn't intended to be used directly, but as part of a use call. The %options available are subject to change:

dbh( $catalog, $options )

Attempts to get you a database handle for the current catalog. If you pass in the optional $catalog variable, it will attempt to use that. If you do not pass it, then it will use the catalog set for the class, or failing that, will attempt to use the catalog registered with the camp if there is only one. If there are more than one catalog, it will give up.

This subroutine uses db_dsn(), db_user(), and db_password() to get the dsn, username, and password respectively for the catalog.

$options is an optional hash ref that contains the options that will be passed directly to the DBI->connect() routine. If nothing is passed in, it will default to using AutoCommit => 1 and RaiseError => 1.

The database handle is not set in the class or cached, you will get a new one each time you call this routine.

db_dsn( $catalog )

Returns the value set in the SQLDSN variable or DatabaseDefault DSN for the catalog specified. $catalog is optional. It uses the smart_variable subroutine, so all of the caveats for that apply here.

db_user( $catalog )

Returns the value set in the SQLUSER variable or DatabaseDefault USER for the catalog specified. It uses the smart_variable subroutine, so all of the caveats for that apply here.

db_password( $catalog )

Returns the value set in the SQLPASS variable or DatabaseDefault PASS for the catalog specified. $catalog is optional. It uses the smart_variable subroutine, so all of the caveats for that apply here.

use_libs

If provided with a true value ("use Camp::Config (use_libs => 1)"), Camp::Config will invoke include_paths() at import time to modify the process' Perl library search path to include the Interchange-aware paths.

no_init

If provided with a true value ("use Camp::Config (no_init => 1,)"), Camp::Config will load in without running its own initialization process, meaning that the Interchange files aren't parsed and the environment isn't validated. Use of this isn't really recommended, but it's there for utilities like chcamp.

BUGS AND LIMITATIONS

This module has tests. It also probably has bugs. It is, after all, software.

AUTHOR

Ethan Rowe and other contributors

LICENSE AND COPYRIGHT

Copyright (C) 2006-2015 End Point Corporation, https://www.endpoint.com/

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see: http://www.gnu.org/licenses/

Commands:

Configuration, API, and Internals:

© 2006–2019 End Point Corporation