Log:
Decouple disk based descend order, config and overlays to allow other sources Ticket #38
This is a major change.
Added:
spine/branches/spine_2_2-devel/lib/Spine/Plugin/Data/
spine/branches/spine_2_2-devel/lib/Spine/Plugin/Data/Disk.pm (contents, props changed)
spine/branches/spine_2_2-devel/lib/Spine/Plugin/Descend/
spine/branches/spine_2_2-devel/lib/Spine/Plugin/Descend/Disk.pm (contents, props changed)
spine/branches/spine_2_2-devel/lib/Spine/Plugin/Overlay/
spine/branches/spine_2_2-devel/lib/Spine/Plugin/Overlay/Disk.pm
- copied, changed from r304, spine/branches/spine_2_2-devel/lib/Spine/Plugin/Overlay.pm
spine/branches/spine_2_2-devel/lib/Spine/Plugin/Overlay/HTTP.pm (contents, props changed)
spine/branches/spine_2_2-devel/tests/Helpers/DescendOrder.pm (contents, props changed)
spine/branches/spine_2_2-devel/tests/spine-core-parselets.t (contents, props changed)
spine/branches/spine_2_2-devel/tests/spine-coreplug-DescendOrder.t (contents, props changed)
spine/branches/spine_2_2-devel/tests/spine-coreplug-Overlay.t (contents, props changed)
spine/branches/spine_2_2-devel/tests/spine-mod-chain.t
- copied unchanged from r305, spine/branches/spine_2_2-devel/tests/spine-chain.t
spine/branches/spine_2_2-devel/tests/spine-mod-key.t (contents, props changed)
spine/branches/spine_2_2-devel/tests/spine-mod-util.t
- copied, changed from r310, spine/branches/spine_2_2-devel/tests/spine-util.t
spine/branches/spine_2_2-devel/tests/spine-plug-Descend_Disk.t (contents, props changed)
spine/branches/spine_2_2-devel/tests/test_root/config/include
spine/branches/spine_2_2-devel/tests/test_root/config_group/
spine/branches/spine_2_2-devel/tests/test_root/config_group/test/
spine/branches/spine_2_2-devel/tests/test_root/config_group/test/include
Deleted:
spine/branches/spine_2_2-devel/tests/spine-chain.t
spine/branches/spine_2_2-devel/tests/spine-util.t
Modified:
spine/branches/spine_2_2-devel/lib/Spine/Data.pm
spine/branches/spine_2_2-devel/lib/Spine/Plugin/Auth.pm
spine/branches/spine_2_2-devel/lib/Spine/Plugin/DescendOrder.pm
spine/branches/spine_2_2-devel/lib/Spine/Plugin/Overlay.pm
spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/Basic.pm
spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/Complex.pm
spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/DNS.pm
spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/Dynamic.pm
spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/JSON.pm
spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/Operator.pm (contents, props changed)
spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/YAML.pm
spine/branches/spine_2_2-devel/lib/Spine/Plugin/SystemInfo.pm
spine/branches/spine_2_2-devel/lib/Spine/Plugin/Templates.pm
spine/branches/spine_2_2-devel/spine-mgmt.conf
spine/branches/spine_2_2-devel/tests/Helpers/Data.pm (contents, props changed)
spine/branches/spine_2_2-devel/tests/Makefile
spine/branches/spine_2_2-devel/tests/spine-core.t
Modified: spine/branches/spine_2_2-devel/lib/Spine/Data.pm
==============================================================================
--- spine/branches/spine_2_2-devel/lib/Spine/Data.pm Tue Mar 30 08:35:44 2010 (r317)
+++ spine/branches/spine_2_2-devel/lib/Spine/Data.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -22,92 +22,91 @@
package Spine::Data;
use strict;
-
use Cwd;
use Data::Dumper;
use File::Basename;
use File::Spec::Functions;
use IO::Dir;
use IO::File;
-use Spine::Constants qw(:basic :plugin);
+use Scalar::Util qw(blessed);
+use Spine::Constants qw(:basic :plugin :keys);
use Spine::Registry;
use Spine::Util;
+use Spine::Key;
use Sys::Syslog;
####### TODO refactor this out
use Template::Exception;
#######
use UNIVERSAL;
-
-our $VERSION = sprintf("%d", q$Revision: 1$ =~ /(\d+)/);
+our $VERSION = sprintf( "%d", q$Revision: 1$ =~ /(\d+)/ );
our $DEBUG = $ENV{SPINE_DATA_DEBUG} || 0;
-our ($PARSER_FILE, $PARSER_LINE, $KEYTT);
+our ( $PARSER_FILE, $PARSER_LINE, $KEYTT );
-our ($DATA_PARSED, $DATA_POPULATED) = (SPINE_NOTRUN, SPINE_NOTRUN);
+our ( $DATA_PARSED, $DATA_POPULATED ) = ( SPINE_NOTRUN, SPINE_NOTRUN );
sub new {
my $class = shift;
- my %args = @_;
+ my %args = @_;
my $registry = new Spine::Registry();
- my $croot = $args{croot} || $args{source}->config_root() || undef;
+ my $croot = $args{croot} || $args{source}->config_root() || undef;
unless ($croot) {
die "No configuration root passed to Spine::Data! Badness!";
}
- my $data_object = bless( { hostname => $args{hostname},
- c_hostname => $args{hostname},
- c_release => $args{release},
- c_verbosity => $args{verbosity} || 0,
- c_quiet => $args{quiet},
- c_version => $args{version} || $::VERSION,
- c_config => $args{config},
- c_croot => $croot,
- }, $class );
+ my $data_object = bless( { hostname => $args{hostname},
+ c_hostname => $args{hostname},
+ c_release => $args{release},
+ c_verbosity => $args{verbosity} || 0,
+ c_quiet => $args{quiet},
+ c_version => $args{version} || $::VERSION,
+ c_config => $args{config},
+ c_croot => $croot, },
+ $class );
- if (not defined($data_object->{c_release}))
- {
+ if ( not defined( $data_object->{c_release} ) ) {
print STDERR 'Spine::Data::new(): we require the config release '
- . 'number!';
+ . 'number!';
return undef;
}
-
- $data_object->_create_hookpoints($registry);
-
+
+ $data_object->_create_hookpoints($registry);
+
# XXX Right now, the driver script handles error reporting
- unless ($data_object->_data() == SPINE_SUCCESS) {
+ unless ( $data_object->_data() == SPINE_SUCCESS ) {
$data_object->{c_failure} = 1;
}
+
return $data_object;
}
-
-sub _create_hookpoints
-{
- my $self = shift;
+sub _create_hookpoints {
+ my $self = shift;
my $registry = shift;
-
- # Let's register the hookable points we're going to be running, we don't
+ # Let's register the hookable points we're going to be running, we don't
# need to do this as they will be created as needed but it makes it clear
# what we expect to use.
# Runtime data discovery. Basically these are keys that will show up in
# $c(a.k.a. the "c" object in a template) that aren't parsed out of the
# configball. This is stuff like c_is_virtual, c_filer_exports, etc.
- $registry->create_hook_point(qw(DISCOVERY/populate
- DISCOVERY/policy-selection));
+ $registry->create_hook_point(
+ qw(DISCOVERY/populate
+ DISCOVERY/policy-selection) );
# The actual parsing of the configball
- $registry->create_hook_point(qw(PARSE/initialize
- PARSE/pre-descent
- PARSE/key
- PARSE/post-descent
- PARSE/complete));
+ $registry->create_hook_point(
+ qw(PARSE/initialize
+ PARSE/start-descent
+ PARSE/pre-descent
+ PARSE/key
+ PARSE/post-descent
+ PARSE/complete) );
-
# Since we are the owner of PARSE/key we need to set up some odering rules.
# This allow an order to be assimed with a single provide given.
# (order bellow)
@@ -125,57 +124,56 @@
my $point = $registry->get_hook_point('PARSE/key');
#### init
# hooks the provide retrieve also provide init
- $point->add_rule(provide => "retrieve",
- provides => [ "init" ]);
- $point->add_rule(provide => "markup",
- provides => [ "init" ],
- succedes => [ "retrieve" ]);
- $point->add_rule(provide => "preprocess",
- provides => [ "init" ],
- succedes => [ "retrieve", "markup" ]);
+ $point->add_rule( provide => "retrieve",
+ provides => ["init"] );
+ $point->add_rule( provide => "markup",
+ provides => ["init"],
+ succedes => ["retrieve"] );
+ $point->add_rule( provide => "preprocess",
+ provides => ["init"],
+ succedes => [ "retrieve", "markup" ] );
#### process
# hooks that provide process come after hooks that provide init
$point->add_rule( provide => "process",
- succedes => [ "init"] );
+ succedes => ["init"] );
$point->add_rule( provide => "complex",
- provides => [ "process" ] );
+ provides => ["process"] );
$point->add_rule( provide => "dynamic",
- provides => [ "process" ],
- succedes => [ "complex" ] );
+ provides => ["process"],
+ succedes => ["complex"] );
$point->add_rule( provide => "simple",
- provides => [ "process" ],
- succedes => [ "complex", "dynamic" ] );
-
- ### finalize
+ provides => ["process"],
+ succedes => [ "complex", "dynamic" ] );
+
+ ### finalize
# hooks that provide finalize come after hooks the provide init and process
$point->add_rule( provide => "finalize",
succedes => [ "init", "process" ] );
$point->add_rule( provide => "tidy",
- provides => [ "finalize" ] );
+ provides => ["finalize"] );
$point->add_rule( provide => "merge",
- provides => [ "finalize" ],
- succedes => [ "tidy" ] );
-
+ provides => ["finalize"],
+ succedes => ["tidy"] );
}
-sub _data
-{
+# kick off the populate and parse sections
+sub _data {
my $self = shift;
- my $rc = SPINE_SUCCESS;
+ my $rc = SPINE_SUCCESS;
my $cwd = getcwd();
- chdir($self->{c_croot});
- $self->{c_croot} = getcwd();
+ chdir( $self->{c_croot} );
+ $self->{c_croot} = getcwd();
- unless ($self->populate() == SPINE_SUCCESS) {
- $self->error('Failure to run populate()!', 'crit');
+ unless ( $self->populate() == SPINE_SUCCESS ) {
+ $self->error( 'Failure to run populate()!', 'crit' );
$rc = SPINE_FAILURE;
}
- unless ($rc != SPINE_SUCCESS) {
- unless ($self->parse() == SPINE_SUCCESS) {
- $self->error('Failure to run parse()!', 'crit');
+ unless ( $rc != SPINE_SUCCESS ) {
+ unless ( $self->parse() == SPINE_SUCCESS ) {
+ $self->error( 'Failure to run parse()!', 'crit' );
$rc = SPINE_FAILURE;
}
}
@@ -184,45 +182,60 @@
return $rc;
}
-
-sub populate
-{
- my $self = shift;
+# This does some initial bootstrapping of data and then builds out a full
+# list of branches to descend
+# The core of this is Spine::Plugins::DescendOrder which implements a specual
+# key. It uses Spine::Plugins::Descend::* to do it's magic in a pluggable way
+sub populate {
+ my $self = shift;
my $registry = new Spine::Registry();
- my $errors = 0;
+ my $errors = 0;
- if ($DATA_POPULATED != SPINE_NOTRUN) {
+ if ( $DATA_POPULATED != SPINE_NOTRUN ) {
return $DATA_POPULATED;
}
# Some truly basic stuff first.
- $self->{c_label} = 'spine-mgmt core';
+ $self->{c_label} = 'spine-mgmt core';
$self->{c_start_time} = time();
- $self->{c_ppid} = $$;
+ $self->{c_ppid} = $$;
# FIXME Should these be moved to Spine::Plugin::Overlay?
- $self->{c_tmpdir} = "/tmp/spine-mgmt." . $self->{c_ppid};
+ $self->{c_tmpdir} = "/tmp/spine-mgmt." . $self->{c_ppid};
$self->{c_tmplink} = "/tmp/spine-mgmt.lastrun";
# Retrieve "internal" config values nesessary for bootstrapping of
# discovery and therefore parsing of the tree.
- $self->cprint("retrieving base settings", 3);
+ $self->cprint( "retrieving base settings", 3 );
+
+ # TODO: this location should probably be based on something passed by
+ # the spine-mgmt executable, this will allow it to be alterd
+ # i.e. there is no reason why this has to be a file uri
+ # XXX: this location will be processed by Spine::Plugins::Data::Disk
$self->{c_internals_dir} = 'spine_internals';
- unless ($self->_get_values($self->{c_internals_dir}, 1)) {
- $self->error('error parsing config within "' .
- $self->{c_internals_dir} . '"', 'crit');
+ if ( $self->read_config_branch(uri => "file:///" . $self->{c_internals_dir},
+ fatal_if_missing => 1) != 0 )
+ {
+ $self->error(
+ 'error parsing config within "' . $self->{c_internals_dir} . '"',
+ 'crit' );
+
# Without this nothing much will work, so rather
# then letting the user find out by an indirectly
# related error we stop now.
return SPINE_FAILURE;
}
- my @dir_list = (ref($self->{'spine_local_internals_dirs'}) eq 'ARRAY')
- ? @{$self->{'spine_local_internals_dirs'}}
- : ($self->{'spine_local_internals_dirs'});
+ # TODO: this should be moved to a compatibility module
+ # and a new key spine_local_internals_uris be used insted
+ my @dir_list =
+ ( ref( $self->{'spine_local_internals_dirs'} ) eq 'ARRAY' )
+ ? @{ $self->{'spine_local_internals_dirs'} }
+ : ( $self->{'spine_local_internals_dirs'} );
foreach my $dir (@dir_list) {
- next unless defined $dir;
- $self->_get_values($dir);
+ next unless defined $dir;
+ $self->read_config_branch( uri => "file:///$dir",
+ fatal_if_missing => 1 );
}
#
@@ -236,19 +249,21 @@
# Spine::Registry::HookPoint::run_hooks() returns the number of
# errors + failures encountered by plugins
- if ($rc != 0) {
+ if ( $rc != 0 ) {
$DATA_POPULATED = SPINE_FAILURE;
- $self->error('DISCOVERY/populate: Failed to run at least one hook!',
- 'crit');
+ $self->error( 'DISCOVERY/populate: Failed to run at least one hook!',
+ 'crit' );
return SPINE_FAILURE;
}
+ # FIXME: This should probably be made part of the above described
+ # spine_local_internals_uris key
# Parse the top level config directory if it exists.
- $self->_get_values($self->{c_croot});
+ $self->read_config_branch( uri => "file:///" . $self->{c_croot} );
# HOOKME Discovery: policy selection
#
- # Using the data we have gathered, construct the directory paths for
+ # Using the data we have gathered, construct the paths for
# our hierarchy.
#
@@ -257,10 +272,11 @@
$rc = $point->run_hooks($self);
- unless ($rc == 0) {
+ unless ( $rc == 0 ) {
$DATA_POPULATED = SPINE_FAILURE;
- $self->error('DISCOVERY/policy-selection: Failed to run at least one'
- . 'hook!', 'crit');
+ $self->error( 'DISCOVERY/policy-selection: Failed to run at least one'
+ . 'hook!',
+ 'crit' );
return SPINE_FAILURE;
}
@@ -269,21 +285,23 @@
return SPINE_SUCCESS;
}
-
-sub parse
-{
- my $self = shift;
+# This will kick off the actaul parsing of keys that are not related to boot
+# strapping. It uses the Spine::Plugins::Data::* plugins to support keys
+# from any source you can think of. Key parsing it's self is part of the
+# Spine::Plugins::Parselet::* code
+sub parse {
+ my $self = shift;
my $registry = new Spine::Registry();
- my $errors = 0;
+ my $errors = 0;
- if ($DATA_PARSED != SPINE_NOTRUN) {
+ if ( $DATA_PARSED != SPINE_NOTRUN ) {
return $DATA_PARSED;
}
# Make sure our discovery phases has been run first since we need a bunch
# of that info for out parsing. Most notably the descend order.
- unless ($self->populate() == SPINE_SUCCESS) {
- $self->error('PARSE: failed to run populate()!', 'crit');
+ unless ( $self->populate() == SPINE_SUCCESS ) {
+ $self->error( 'PARSE: failed to run populate()!', 'crit' );
goto parse_failure;
}
@@ -296,42 +314,18 @@
my $rc = $point->run_hooks($self);
- if ($rc != 0) {
- $self->error('PARSE/initialize: Failed to run at least one hook!',
- 'crit');
+ if ( $rc != 0 ) {
+ $self->error( 'PARSE/initialize: Failed to run at least one hook!',
+ 'crit' );
goto parse_failure;
}
# Gather config data from the entire hierarchy.
- $self->cprint('descending hierarchy', 3);
- foreach my $branch (@{$self->{c_hierarchy}})
- {
- # HOOKME Parse: pre-policy descent
- $point = $registry->get_hook_point('PARSE/pre-descent');
-
- $rc = $point->run_hooks($self);
-
- if ($rc != 0) {
- $self->error('PARSE/pre-descent: Failed to run at least one hook!',
- 'crit');
+ $self->cprint( 'descending hierarchy', 3 );
+ foreach my $branch ( @{$self->getvals(SPINE_HIERARCHY_KEY, 1)} ) {
+ if ( $self->read_config_branch($branch) != 0 ) {
goto parse_failure;
}
-
- if (not $self->get_configdir($branch)) {
- return 0;
- }
-
- # HOOKME Parse: post-policy descent
- $point = $registry->get_hook_point('PARSE/post-descent');
-
- $rc = $point->run_hooks($self);
-
- if ($rc != 0) {
- $self->error('PARSE/post-descent: Failed to run at least one '
- . 'hook!', 'crit');
- goto parse_failure;
- }
-
}
# HOOKME Parse: parse complete
@@ -339,336 +333,329 @@
$rc = $point->run_hooks($self);
- if ($rc != 0) {
- $self->error('PARSE/complete: Failed to run at least one hook!',
- 'crit');
+ if ( $rc != 0 ) {
+ $self->error( 'PARSE/complete: Failed to run at least one hook!',
+ 'crit' );
goto parse_failure;
}
- $self->print(1, "parse complete");
+ $self->print( 1, "parse complete" );
return SPINE_SUCCESS;
- parse_failure:
+ parse_failure:
$DATA_PARSED = SPINE_FAILURE;
return SPINE_FAILURE;
}
-
-#
-# Public interface for get_values. Very possibly unnecessary at the moment.
-#
-sub get_values
-{
- return _get_values(@_);
-}
-
-
-sub _get_values
-{
- my $self = shift;
- my $directory = shift;
- my $fatal_if_missing = shift;
- my $keys_dir = $self->getval_last('config_keys_dir') || 'config';
-
- unless (defined $directory) {
- $self->error("_get_values(): no path passed to method", 'crit');
- return SPINE_FAILURE;
- }
-
- $directory = catdir($directory, $keys_dir);
-
- # It's perfectly OK to have an overlay-only tree or directory in the
- # descend order that is only a hierachical organizer that doesn't require
- # any config variables be set
- # XXX: unless fatal_if_missing it true
- unless (-d $directory) {
- if ($fatal_if_missing) {
- $self->error("_get_values(): \" " . $self->{c_croot} .
- "/$directory\" does not exists",
- 'crit');
- return SPINE_FAILURE
- }
- return SPINE_SUCCESS;
+# public interface to allow a branch to be read. That means all the keys
+# within that branch will be within Spine::Data once done.
+# See Spine::Plugins::Data::* for implementaions
+sub read_config_branch {
+ my $self = shift;
+ my $branch;
+ if ( scalar(@_) > 1 ) {
+ $branch = {@_};
+ } else {
+ $branch = shift;
}
- # Iterate through each file in a hierarchial endpoint and
- # read the contents to extract values.
- my $dir = new IO::Dir($directory);
+ my $registry = new Spine::Registry();
- unless (defined($dir)) {
- $self->error("_get_values(): failed to open $directory: $!", 'crit');
- return SPINE_FAILURE;
- }
+ # HOOKME Parse: branch descent
+ my $point = $registry->get_hook_point('PARSE/branch');
- my @files = $dir->read();
- $dir->close();
+ $self->cprint( "processing branch (" . $branch->{uri} . ")", 3 );
- foreach my $keyfile (sort(@files))
- {
- # Key names beginning with c_ are reserved for values
- # that are automatically populated by the this module.
- my $keyname = basename($keyfile);
- if ($keyname eq '.' or $keyname eq '..') {
- next;
- }
+ return $point->run_hooks( $self, $branch );
- if ($keyname =~ m/(?:(?:^(?:\.|c_\#).*)|(?:.*(?:~|\#)$))/) {
- $self->error("ignoring $directory/$keyname because of lame"
- . ' file name', 'err');
- next;
- }
-
- # Read the contents of a file. Filename is stored
- # as the key, where value(s) are the contents.
- my $value = $self->_read_keyfile(catfile($directory, $keyfile),
- $keyname);
-
- if (not defined($value)) {
- if ($fatal_if_missing) {
- $self->error("_get_values(): \" " . $self->{c_croot} .
- "/$keyfile\" parse error",
- 'crit');
- }
-
- return SPINE_FAILURE;
- }
-
- $self->{$keyname} = $value;
- }
+}
- return SPINE_SUCCESS;
-}
+# a wrapper for read_key that does a little more checking and does not support
+# current data unless it's key'ed into Spine::Data and a keyname => "foo" is
+# given.
+# simple usage read_keyuri(uri => "some://uri")
+# supports all standard Spine::Key metadata (see parselets)
+sub read_keyuri {
+ my $self = shift;
+ my ( $item, $keyname );
-# This is a public interface to _read_keyfile below. An underscore prefix on
-# a member variable or method traditionally means that member is a privately
-# scoped method and shouldn't be called outside the object or its inheritors.
-#
-# rtilder Thu Jun 8 13:05:55 PDT 2006
-sub read_keyfile
-{
- my $self = shift;
+ if ( scalar(@_) > 1 ) {
+ $item = {@_};
+ } else {
+ $item = shift;
+ }
- my $values = $self->_read_keyfile(@_);
+ unless ( ref($item) eq "HASH" && exists $item->{uri} ) {
+ $self->error( "call to read_keyuri without a uri option", "err" );
+ return undef;
+ }
- unless (defined($values)) {
+ if ( exists $item->{keyname} ) {
+ $keyname = $item->{keyname} if exists $item->{keyname};
+ $item->{description} = "$keyname key"
+ unless exists $item->{description};
+ }
+
+ # read the key with out item asking kindly to get
+ # an object back
+ my $values = $self->read_key($item);
+
+ # if the current key was a Spine::Key then the return value
+ # will be the same.
+ if (defined $values && blessed $values && $values->isa("Spine::Key")) {
+ $values = $values->get();
+ }
+ unless ( defined($values) ) {
return wantarray ? () : undef;
}
- return wantarray ? @{$values} : $values;
+ # TODO: This is really backwards compatability
+ # and should be removed
+ if ( ref($values) eq "ARRAY" ) {
+ return wantarray ? @{$values} : $values;
+ }
+
+ return $values;
+}
+
+# read a new_key (can be a hash or a Spine::Key object)
+# with optinal current key / data
+# if return_obj is 1 then it will always return a Spine::Key
+# if keyname is set within new_key then it will also store it
+# within the Spine::Data tree
+sub read_key {
+ my ( $self, $new_key, $current, $return_obj) = @_;
+
+ # unless the key is already an obj we will return raw data
+ # that is unless the caller has said it really want the obj
+ # XXX: one day perhaps all keys will be objects?? Probably not that bad?
+ # considering that the get hidden behind a tie when in Spine::Data;
+ $return_obj = 0 unless defined $return_obj;
+
+ # must have something to work on
+ if (not defined $new_key) {
+ $self->error("call to Spine::Data::read_key without data", "warn");
+ return undef;
+ } elsif (ref ($new_key) eq "HASH") {
+ # if the new_key is just a hash then we assume that
+ # we need to create a standard Spine::Key using the hash
+ # content as the metadata
+ my $meta_data = $new_key;
+ $new_key = new Spine::Key;
+ $new_key->metadata_set(%{$meta_data});
+ } elsif (not blessed($new_key) || not $new_key->isa("Spine::Key")) {
+ # something was passed but not anything we want....
+ $self->error("bad argument given to Spine::Data::read_key", "warn");
+ return undef;
+ } else {
+ # we were given a Spine::Key so we will return one!
+ $return_obj = 1;
+ }
+
+ # this could result in undef
+ my $keyname = $new_key->metadata("keyname");
+
+ # if we have no current object but have a keyname that might
+ # result in one then try to get it
+ unless (defined $current) {
+ $current = $self->getkey($keyname) if (defined $keyname);
+ }
+
+ # if current is defined we work out if it's raw data or an object
+ # if it's data then we put it within a Spine::Key object
+ if (defined $current) {
+ if (blessed($current) && $current->isa("Spine::Key")) {
+ # since the passed in current key was a Spine::Key we will
+ # return a Spine::Key
+ $return_obj = 1;
+ } else {
+ $current = new Spine::Key($current);
+ }
+ }
+
+ # at this point we have a least one src Spine::Key and posiably
+ # a destination/current Spine::Key. We pass this to the hook point
+ # return_item will be undef if there was an error or a Spine::Key
+ my $return_item = $self->_call_key_hook($new_key, $current);
+
+ # XXX: should we log an error here?
+ return undef unless defined $return_item;
+
+ # Will we return an object or it's data? If the caller passed in objects
+ # then we assume they expect to get some back!
+ $return_item = $return_obj ? $return_item : $return_item->get();
+
+ # if the caller passed in the keyname then we save the key under that name
+ if (defined $keyname) {
+ $self->set($keyname, $return_item);
+ }
+
+ return $return_item;
}
-
-sub _read_keyfile
-{
- my $self = shift;
- my ($file, $keyname) = @_;
+# the raw read_key call all items passed in must be Spine::Key objects
+# always returns a Spine::Key or undef if there was an issue
+# XXX: I don't think this needs to be publically avaliable, but we are open to
+# sugestions as to why it might be help full
+sub _call_key_hook {
+ my ($self, $new_key, $current) = @_;
# parse the key
# HOOKME Parselet expansion
my $registry = new Spine::Registry;
- my $point = $registry->get_hook_point('PARSE/key');
- my $obj = {
- source => "file:$file",
- keyname => $keyname
- };
-
- my (undef, $rc, undef) = $point->run_hooks_until(PLUGIN_STOP, $self, $obj);
-
- if ($rc == PLUGIN_FATAL) {
- # XXX: is it ok to warn here? Will probably help the user.
- $self->error("could not parse the \"$keyname\" key ($file)", 'warn');
- return undef;
- }
- return $obj->{obj};
-}
+ my $point = $registry->get_hook_point('PARSE/key');
-sub get_configdir
-{
- my $self = shift;
- my $branch = shift;
+ my $sref = undef;
+ my $ret_key = \$sref;
- # It's perfectly alright if the directory doesn't exist.
- #
- # rtilder Wed Jul 5 10:52:05 PDT 2006
- unless (-d $branch) {
- return SPINE_SUCCESS;
- }
+ my ( undef, $rc, undef ) =
+ $point->run_hooks_until( PLUGIN_STOP, $self, $new_key,
+ $current, $ret_key);
- if (not $self->_get_values($branch)) {
- $self->error("required directory [$branch] has errors",
- 'err');
- return SPINE_FAILURE;
+ if ( ($rc & PLUGIN_FATAL) == PLUGIN_FATAL) {
+ return undef;
}
-
- # Store the paths we actually managed to descend
- # in the exact order that we did so.
- push(@{$self->{c_descend_order}}, $branch);
-
- return SPINE_SUCCESS;
+
+ # just in case ret_key didn't get set during the run i.e. no plugins loaded
+ return undef unless (defined $ret_key);
+
+ return $$ret_key;
}
-
-sub get_config_group
-{
+sub getval {
my $self = shift;
- my $group = shift;
+ my $key = shift;
- my $group_dir = catdir($self->getval_last('include_dir'), $group);
+ $self->print( 4, "getval -> $key" );
+ return undef unless ( exists $self->{$key} );
- return $self->get_configdir($group_dir);
-}
-
-
-sub getval
-{
- my $self = shift;
- my $key = shift;
- $self->print(4, "getval -> $key");
- return undef unless (exists $self->{$key});
-
- if ((ref $self->{$key}) eq "ARRAY")
- {
- return $self->{$key}[0];
- }
- else
- {
- return $self->{$key};
+ if ( ( ref $self->{$key} ) eq "ARRAY" ) {
+ return $self->{$key}[0];
+ } else {
+ return $self->{$key};
}
}
# like getval but will always return the key
# never the first element
-sub getkey
-{
+sub getkey {
my $self = shift;
- my $key = shift;
- $self->print(4, "getkey -> $key");
- return undef unless (exists $self->{$key});
- return $self->{$key};
+ my $key = shift;
+ $self->print( 4, "getkey -> $key" );
+ return undef unless ( exists $self->{$key} );
+ return $self->{$key};
+}
+# This simplifys most of spine but hiding special keys from 90% of the code
+# anything that really really wants the key should call getkey to be 100%
+# that it gets what it expects
+sub hidekey {
+ my ( $self, $keyname, $obj ) = @_;
+ $self->{$keyname} = "";
+ tie $self->{$keyname}, "Spine::Data::HiddenKey", $obj;
}
sub getval_last {
my $self = shift;
- my $key = shift;
- $self->print(4, "getval -> $key");
- return undef unless (exists $self->{$key});
+ my $key = shift;
- if ((ref $self->{$key}) eq "ARRAY")
- {
- return $self->{$key}[-1];
- }
- else
- {
- return $self->{$key};
+ $self->print( 4, "getval -> $key" );
+ return undef unless ( exists $self->{$key} );
+
+ if ( ( ref $self->{$key} ) eq "ARRAY" ) {
+ return $self->{$key}[-1];
+ } else {
+ return $self->{$key};
}
}
-
-sub getvals
-{
- my $self = shift;
- my $key = shift;
+sub getvals {
+ my $self = shift;
+ my $key = shift;
my $force = shift || 0;
- $self->print(4, "getvals -> $key");
+ $self->print( 4, "getvals -> $key" );
- unless ($key && exists $self->{$key})
- {
- if ($force)
- {
+ unless ( $key && exists $self->{$key} ) {
+ if ($force) {
return [];
- }
- else
- {
+ } else {
return undef;
}
}
- if ((ref $self->{$key}) eq "ARRAY")
- {
- return $self->{$key};
- }
- else
- {
- return [$self->{$key}];
+ if ( ( ref $self->{$key} ) eq "ARRAY" ) {
+ return $self->{$key};
+ } else {
+ return [$self->{$key}];
}
}
-
-sub getvals_as_hash
-{
+sub getvals_as_hash {
my $self = shift;
- my $key = shift;
+ my $key = shift;
- $self->print(4, "getval_as_hash -> $key");
+ $self->print( 4, "getval_as_hash -> $key" );
- return undef unless($key && exists($self->{key}));
+ return undef unless ( $key && exists( $self->{$key} ) );
+
+ if ( ref($self->{$key}) eq 'ARRAY' ) {
- if (ref($self->{$key}) eq 'ARRAY')
- {
# Make sure it's an array with an even number of elements(greater than
# zero)
- my $oe = scalar(@{$self->{$key}});
+ my $oe = scalar( @{$self->{$key}} );
- if ($oe and not ($oe % 2))
- {
+ if ( $oe and not( $oe % 2 ) ) {
my %vals_as_hash = @{$self->{$key}};
return \%vals_as_hash;
}
+ } elsif ( ref($self->{$key}) eq 'HASH' ) {
+ return $self->{$key};
}
return undef;
}
-
-sub getvals_by_keyname
-{
+sub getvals_by_keyname {
my $self = shift;
my $key_re = shift || undef;
my @matching_vals = ();
- $self->print(4, "getvals_by_keyname -> $key_re");
+ $self->print( 4, "getvals_by_keyname -> $key_re" );
- foreach my $key (keys(%{$self})) {
- if ($key =~ m/$key_re/) {
- push @matching_vals, $self->{key};
- }
+ foreach my $key ( keys( %{$self} ) ) {
+ if ( $key =~ m/$key_re/ ) {
+ push @matching_vals, ${ $self->{$key} };
+ }
}
# Sorted for minimal changes in diffs of templates
@matching_vals = sort @matching_vals;
- return (scalar @matching_vals > 0) ? \@matching_vals : undef;
+ return ( scalar @matching_vals > 0 ) ? \@matching_vals : undef;
}
-
-sub search
-{
- my $self = shift;
+sub search {
+ my $self = shift;
my $regex = shift;
- my @keys = grep(/$regex/, keys %{$self});
+ my @keys = grep( /$regex/, keys %{$self} );
return \@keys;
}
-
-sub set_label
-{
- my $self = shift;
+sub set_label {
+ my $self = shift;
my $label = shift;
- if ($label)
- {
- $self->{c_label} = $label;
- return 1;
+ if ($label) {
+ $self->{c_label} = $label;
+ return 1;
}
return 0;
}
-
#
# This method checks its call stack to make sure that it isn't being called
# from anywhere inside Template::Toolkit and prevents any template from
@@ -678,183 +665,209 @@
# have a Spine::Data::TemplateProxy class or similar that prevents a
# template from modifying any data in it via TIE. Shouldn't be too
# difficult, come to think of it.
+# FIXME this needs a real rethink....
#
-sub set
-{
- my $self = shift;
- my $key = shift;
+sub set {
+ my $self = shift;
+ my $key = shift;
my $in_template = 0;
# We should never get a stack deeper than 30
- foreach my $i (1 .. 30) {
+ foreach my $i ( 1 .. 30 ) {
my @frame = caller($i);
- if ($frame[3] =~ m/^Template/) {
+ last unless scalar @frame;
+
+ if ( $frame[3] =~ m/^Template/ ) {
$in_template = 1;
last;
}
}
-
####### TODO refactor this out
# FIXME This is pretty lame way to differentiate which context the
# template is running in. (Perhaps use Hash::Util::lock_hash())
#
# This is ok as long as we're in a key template instance but it's not ok
# if we're in an overlay template instance. Ain't life grand?
- if ($in_template and not defined($Spine::Plugin::Templates::KEYTT)) {
- $self->error("We've got an overlay template that's trying to call "
- . "Spine::Data::set($key). This is bad.", 'err ');
- die (Template::Exception->new('Spine::Data::set()',
- 'Overlay template trying to call ' .
- "Spine::Data::set($key). Bad template"));
+ if ( $in_template and not defined($Spine::Plugin::Templates::KEYTT) ) {
+ $self->error( "We've got an overlay template that's trying to call "
+ . "Spine::Data::set($key). This is bad.",
+ 'err ' );
+ die(Template::Exception->new( 'Spine::Data::set()',
+ 'Overlay template trying to call '
+ . "Spine::Data::set($key). Bad template"
+ ) );
}
-
#
# If it's a reference of any kind, don't make it an array. This permits
# plugins to call $c->set('c_my_plugin', $my_plugin_obj). ONLY do this if
# there's only one argument passed in. Otherwise, push them in as an array
- #
- # This bit me in the ass with the auth plugin's _grep_hash_element() method
- # while it spewed "Out of memory" to the console.
- #
- # rtilder Tue Dec 19 15:52:52 PST 2006
- if (ref($_[0]) and scalar(@_) == 1) {
- $self->{$key} = $_[0];
- } elsif (exists($self->{$key})) {
- push @{ $self->{$key} }, @_;
+ if ( ref( $_[0] ) and scalar(@_) == 1 ) {
+ # if it's a special Spine::Key then we tie it in to hide it's
+ # implementation from most of the code you can get the real key
+ # by calling Spine::Data::getkey (i prommise)
+ if ( blessed $_[0] && $_[0]->isa("Spine::Key") ) {
+ $self->hidekey( $key, $_[0] );
+ } else {
+ $self->{$key} = $_[0];
+ }
+ } elsif (scalar(@_) > 1) {
+ $self->{$key} = [@_];
} else {
- $self->{$key} = [ @_ ];
+ $self->{$key} = $_[0];
}
+ ########
+ #XXX: removed, don't think this is needed as parselets do this!
+ # the idea that set will do some kind of merge it not a safe one
+ # and not expandable!
+ #elsif ( exists( $self->{$key} ) && ref($self->{$key}) eq "ARRAY" ) {
+ # push @{ $self->{$key} }, @_;
+ #} else {
+ # $self->{$key} = [@_];
+ #}
+ ########
# TT gets awfully confused by return values
- $in_template ? return : return 1;
+ $in_template ? return: return 1;
}
-
-sub check_exec
-{
- my $self = shift;
- foreach my $binary (@_)
- {
- return 0 unless (defined $binary);
- $self->print(4, "checking binary $binary");
- if ( ! -x "$binary" )
- {
- $self->error("binary $binary is unavailable: $!", "crit");
- return 0;
- }
- }
- return 1;
-}
-
-
-sub cprint
-{
+# TODO: remove, this should be part of Spine::Util.
+#sub check_exec {
+# my $self = shift;
+# foreach my $binary (@_) {
+# return 0 unless ( defined $binary );
+# $self->print( 4, "checking binary $binary" );
+# if ( !-x "$binary" ) {
+# $self->error( "binary $binary is unavailable: $!", "crit" );
+# return 0;
+# }
+# return 1;
+# }
+#}
+
+# TODO: make plugabble, and probably move to a singleton logging module
+# so that you don't have to pass Spine::Data about all the time
+sub cprint {
my $self = shift;
- my ($msg, $level) = @_;
+ my ( $msg, $level ) = @_;
my $log_to_syslog = shift || 1;
- if ($level <= $self->{c_verbosity})
- {
- print $self->{c_label}, ": $msg\n"
- unless $self->{c_quiet};
+ if ( $level <= $self->{c_verbosity} ) {
+ print $self->{c_label}, ": $msg\n"
+ unless $self->{c_quiet};
- syslog("info", "$msg")
- if ( not $self->{c_dryrun} or $log_to_syslog );
+ syslog( "info", "$msg" )
+ if ( not $self->{c_dryrun} or $log_to_syslog );
}
}
-
-sub print
-{
+sub print {
my $self = shift;
- my $lvl = shift || 0;
+ my $lvl = shift || 0;
- if ($lvl <= $self->{c_verbosity})
- {
-# print $self->{c_label}, '[', join('::', caller()), ']: ', @_, "\n";
- print $self->{c_label}, ': ', @_, "\n"
- unless $self->{c_quiet};
+ if ( $lvl <= $self->{c_verbosity} ) {
+
+ # print $self->{c_label}, '[', join('::', caller()), ']: ', @_, "\n";
+ print $self->{c_label}, ': ', @_, "\n"
+ unless $self->{c_quiet};
}
}
-
-sub log
-{
+sub log {
my $self = shift;
- my $msg = shift;
+ my $msg = shift;
- if (not $self->{c_dryrun}) {
- syslog('info', "$msg");
+ if ( not $self->{c_dryrun} ) {
+ syslog( 'info', "$msg" );
}
}
-
-sub error
-{
+sub error {
my $self = shift;
- my ($msg, $level) = @_;
- $level = 'err' unless ($level =~ m/
+ my ( $msg, $level ) = @_;
+ $level = 'err'
+ unless (
+ $level =~ m/
alert|crit|debug|emerg|err|error|
info|notice|panic|warning|warn
/xi );
$msg =~ tr/\n/ -- /;
# needed for syslog
- $msg = "warning" if ($msg eq 'warn');
+ $msg = "warning" if ( $msg eq 'warn' );
- unless ($self->{c_verbosity} == -1)
- {
- print STDERR $self->{c_label} . ": \[$level\] $msg\n";
+ unless ( $self->{c_verbosity} == -1 ) {
+ print STDERR $self->{c_label} . ": \[$level\] $msg\n";
}
- syslog("$level", "$msg")
- unless $self->{c_dryrun};
- push(@{$self->{c_errors}}, $msg);
+ syslog( "$level", "$msg" )
+ unless $self->{c_dryrun};
+ push( @{ $self->{c_errors} }, $msg );
}
-
-sub util
-{
+# TODO: make pluggable rather then just calling raw utils
+sub util {
my $self = shift;
my $util = shift;
no strict 'refs';
- my $return = &{"Spine::Util::" . $util}(@_);
+ my $return = &{ "Spine::Util::" . $util }(@_);
use strict 'refs';
- if (not defined $return)
- {
- my $pargs = join(" ", @_);
- $self->error("$util failed to execute with args: $pargs", 'crit');
+ if ( not defined $return ) {
+ my $pargs = join( " ", @_ );
+ $self->error( "$util failed to execute with args: $pargs", 'crit' );
}
return $return;
}
-
-sub get_release
-{
+sub get_release {
return (shift)->{c_release};
}
-
-sub debug
-{
+sub debug {
my $lvl = shift;
- if ($DEBUG >= $lvl) {
+ if ( $DEBUG >= $lvl ) {
print STDERR "DATA DEBUG($lvl): ", @_, "\n";
}
}
-sub get_plugin_version
-{
- my $self = shift;
+sub get_plugin_version {
+ my $self = shift;
my $plugin_name = shift;
- my $registry = new Spine::Registry();
+ my $registry = new Spine::Registry();
return $registry->get_plugin_version($plugin_name);
}
1;
+
+# this is used to hide Spine::Key object in the Spine::Data tree
+# only a call from Spine::Data::getkey will result in the obj being
+# returned
+package Spine::Data::HiddenKey;
+
+sub TIESCALAR {
+ my $class = shift;
+ my $real = shift;
+ my $ref_to_real = \$real;
+ return bless $ref_to_real, $class;
+}
+
+sub FETCH {
+ my ( undef, undef, undef, $sub ) = caller(1);
+
+ # allow getkey to
+ return ${ +shift } if ( $sub eq "Spine::Data::getkey" );
+
+ # otherwise return the nice version
+ return ${ ${ +shift }->data_getref() };
+}
+
+sub STORE {
+ ${ +shift }->set(shift);
+}
+
+1;
Modified: spine/branches/spine_2_2-devel/lib/Spine/Plugin/Auth.pm
==============================================================================
--- spine/branches/spine_2_2-devel/lib/Spine/Plugin/Auth.pm Tue Mar 30 08:35:44 2010 (r317)
+++ spine/branches/spine_2_2-devel/lib/Spine/Plugin/Auth.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -220,11 +220,12 @@
next;
}
- my $values = $c->read_keyfile($keyfile);
+ my $values = $c->read_keyuri(uri => "file:///$keyfile",
+ description => "file:///$keyfile Auth key");
- # read_keyfile() returns an undef when there's an error
+ # read_keyuri() returns an undef when there's an error
unless (defined($values)) {
- $c->error("Spine::Data::read_keyfile() failed on $keyfile!",
+ $c->error("Spine::Data::read_keyuri() failed on $keyfile!",
'crit');
die('Bad data');
}
@@ -313,11 +314,12 @@
my $keyname = basename($keyfile);
- my $values = $c->read_keyfile($keyfile);
+ my $values = $c->read_keyuri(uri => "file:///$keyfile",
+ description => "file:///$keyfile Auth key");
- # read_keyfile() returns an undef when there's an error
+ # read_keyuri() returns an undef when there's an error
unless (defined($values)) {
- $c->error("Spine::Data::read_keyfile() failed on auth_groups/$keyname!",
+ $c->error("Spine::Data::read_keyuri() failed on auth_groups/$keyname!",
'crit');
die('Bad data');
}
@@ -353,11 +355,12 @@
my $keyfile = "$directory/$map";
my $keyname = basename($keyfile);
- my $values = $c->read_keyfile($keyfile);
+ my $values = $c->read_keyuri(uri => "file:///$keyfile",
+ description => "file:///$keyfile Auth key");
- # read_keyfile() returns an undef when there's an error
+ # read_keyuri() returns an undef when there's an error
unless (defined($values)) {
- $c->error("Spine::Data::read_keyfile() failed on $map!", 'crit');
+ $c->error("Spine::Data::read_keyuri() failed on $map!", 'crit');
die('Bad data');
}
Added: spine/branches/spine_2_2-devel/lib/Spine/Plugin/Data/Disk.pm
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ spine/branches/spine_2_2-devel/lib/Spine/Plugin/Data/Disk.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -0,0 +1,149 @@
+# -*- mode: perl; cperl-continued-brace-offset: -4; indent-tabs-mode: nil; -*-
+# vim:shiftwidth=2:tabstop=8:expandtab:textwidth=78:softtabstop=4:ai:
+
+# $Id$
+
+#
+# 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.
+#
+# 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/>.
+#
+# (C) Copyright Ticketmaster, Inc. 2007
+#
+
+use strict;
+
+package Spine::Plugin::Data::Disk;
+use base qw(Spine::Plugin);
+use Spine::Constants qw(:plugin);
+use File::Spec::Functions;
+use File::Basename;
+use Spine::Util qw(simple_exec do_rsync mkdir_p octal_conv uid_conv gid_conv);
+
+our ( $VERSION, $DESCRIPTION, $MODULE, $DONTDELETE, $TMPDIR, @ENTRIES );
+
+$VERSION = sprintf( "%d", q$Revision$ =~ /(\d+)/ );
+$DESCRIPTION = "Disk based data.";
+
+$MODULE = { author => 'oss...@ticketmaster.com',
+ description => $DESCRIPTION,
+ version => $VERSION,
+ hooks => {
+ "PARSE/branch" => [ { name => "load_disk_config",
+ code => \&parse_branch } ], } };
+
+use Spine::Constants qw(:basic :plugin);
+
+sub parse_branch {
+ my $c = shift;
+ my $branch = shift;
+
+ my $fatal =
+ exists $branch->{fatal_if_missing}
+ ? exists $branch->{fatal_if_missing}
+ : 0;
+
+ return PLUGIN_SUCCESS unless substr( $branch->{uri}, 0, 5 ) eq "file:";
+
+ my ( undef, $descend_item ) = ( $branch->{uri} =~ m%file://([^/]*)/(.*)% );
+
+ ($branch) = ( $branch->{uri} =~ m%file://[^/]*/(.*)% );
+
+ unless ( -d $branch ) {
+ # is it ok if it's missing?
+ if ($fatal) {
+ $c->error( "required branch is missing \"$branch\"", 'crit' );
+ return PLUGIN_FATAL;
+ }
+ return PLUGIN_SUCCESS;
+ }
+
+ if ( not _get_values( $c, $branch, $fatal ) ) {
+ return PLUGIN_ERROR;
+ }
+
+#### XXX removed as this is pointless now that the c_hierachy key tracks this
+# # Store the paths we actually managed to descend
+# # in the exact order that we did so.
+# push( @{ $c->{c_descend_order} }, $branch );
+
+ return PLUGIN_SUCCESS;
+}
+
+sub _get_values {
+ my $c = shift;
+ my $directory = shift;
+ my $fatal = shift;
+ my $keys_dir = $c->getval_last('config_keys_dir') || 'config';
+
+ unless ( defined $directory ) {
+ $c->error( "_get_values(): no path passed to method", 'crit' );
+ return SPINE_FAILURE;
+ }
+
+ $directory = catdir( $directory, $keys_dir );
+
+ unless ( -d $directory ) {
+ if ($fatal) {
+ $c->error( "required config location is missing \"$directory\"",
+ 'crit' );
+ return SPINE_FAILURE;
+ }
+ return SPINE_SUCCESS;
+ }
+
+ # Iterate through each file in a hierarchial endpoint and
+ # read the contents to extract values.
+ my $dir = new IO::Dir($directory);
+
+ unless ( defined($dir) ) {
+ $c->error( "_get_values(): failed to open $directory: $!", 'crit' );
+ return SPINE_FAILURE;
+ }
+
+ my @files = $dir->read();
+ $dir->close();
+
+ foreach my $keyfile ( sort(@files) ) {
+
+ # Key names beginning with c_ are reserved for values
+ # that are automatically populated by the this module.
+ my $keyname = basename($keyfile);
+ if ( $keyname eq '.' or $keyname eq '..' ) {
+ next;
+ }
+
+ if ( $keyname =~ m/(?:(?:^(?:\.|c_\#).*)|(?:.*(?:~|\#)$))/ ) {
+ $c->error(
+ "ignoring $directory/$keyname because of lame" . ' file name',
+ 'err' );
+ next;
+ }
+
+ $keyfile = "file:///" . catfile( $directory, $keyfile );
+
+ # Read the contents of a file. Filename is stored
+ # as the key, where value(s) are the contents.
+ my $value = $c->read_keyuri( uri => $keyfile,
+ keyname => $keyname,
+ description => "$keyfile key" );
+
+ if ( not defined($value) ) {
+ $c->error( "read_keyuri: \"$keyfile\" parse error", 'crit' );
+
+ return SPINE_FAILURE;
+ }
+ }
+
+ return SPINE_SUCCESS;
+}
+
+1;
Added: spine/branches/spine_2_2-devel/lib/Spine/Plugin/Descend/Disk.pm
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ spine/branches/spine_2_2-devel/lib/Spine/Plugin/Descend/Disk.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -0,0 +1,110 @@
+# -*- Mode: perl; cperl-continued-brace-offset: -4; indent-tabs-mode: nil; -*-
+# vim:shiftwidth=2:tabstop=8:expandtab:textwidth=78:softtabstop=4:ai:
+
+# $Id$
+
+#
+# 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.
+#
+# 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/>.
+#
+# (C) Copyright Ticketmaster, Inc. 2007
+#
+
+use strict;
+
+package Spine::Plugin::Descend::Disk;
+use base qw(Spine::Plugin);
+use Spine::Constants qw(:plugin :keys);
+use Spine::Data;
+use Spine::Key::Blank;
+use Spine::Plugin::DescendOrder;
+use Spine::Plugin::Interpolate;
+
+our ( $VERSION, $DESCRIPTION, $MODULE, $CURRENT_DEPTH, $MAX_NESTING_DEPTH );
+
+$VERSION = sprintf( "%d", q$Revision$ =~ /(\d+)/ );
+$DESCRIPTION = "process disk based includes/branches";
+
+$MODULE = { author => 'oss...@ticketmaster.com',
+ description => $DESCRIPTION,
+ version => $VERSION,
+ hooks => {
+ 'DISCOVERY/Descend/resolve' => [
+ { name => 'resolve_disk_descend',
+ code => \&resolve,
+ provides => ["disk_descend"] }
+ ],
+ "DISCOVERY/populate" => [
+ { name => 'reserve_include_key',
+ code => \&reserve_key, }, ], }
+ };
+use File::Spec::Functions;
+
+sub resolve {
+ my ( $c, $key, $item ) = @_;
+
+ # deal with legacy dirs that do not have proper uris
+ # TODO: depreciate this once everyone uses uri's, should probably move to
+ # a separate fixup plugin!
+ unless ( $item->{uri} =~ m%[^:]+://% ) {
+ # if the item has succedes we assume that it's
+ # a config_group since it has a parent branch
+ if ( exists $item->{dependencies}->{succedes} ) {
+ $item->{uri} = "file:///"
+ . catdir( $c->getval_last('include_dir'), $item->{uri} );
+ } else {
+ $item->{uri} = "file:///$item->{uri}";
+ }
+ }
+
+ # we only deal with file URIs
+ return PLUGIN_SUCCESS unless substr( $item->{uri}, 0, 5 ) eq "file:";
+
+ my ( undef, $descend_item ) = ( $item->{uri} =~ m%file://([^/]*)/(.*)% );
+ my $croot = $c->getval('c_croot');
+
+ unless ( $descend_item =~ m#^/# ) {
+ $descend_item = catfile( $croot, $descend_item );
+ }
+
+ # TODO: this isn't very nice. Taken from the old code. Should be based on a
+ # key. Also the first include is better then the second.
+ foreach my $path (qw(include config/include)) {
+ my $inc_file = catfile( $descend_item, $path );
+
+ # An empty set is perfectly acceptable
+ unless ( -f $inc_file ) {
+ next;
+ }
+
+ # add in the item to $key, noteing that it succedes it's parent
+ # by passing a merge parameter
+ # XXX: might be worth moving this to a function within the DescendOrder
+ # plugin to hide the implementation...
+ $c->read_key( { uri => "file:///$inc_file",
+ description => "file:///$inc_file descend key",
+ operators => [ [ 'merge', $item->{name} ] ], },
+ $c->getkey(SPINE_HIERARCHY_KEY) );
+ }
+}
+
+# Since the include keys get processed from the same location as config keys
+# the resulting data seen by the user in printdata makes little sense. Actauly
+# the data within include is pointless. So lets make sure it stays blank no
+# matter what!
+sub reserve_key {
+ my $c = shift;
+ $c->set( "include", new Spine::Key::Blank );
+ return PLUGIN_SUCCESS;
+}
+
+1;
Modified: spine/branches/spine_2_2-devel/lib/Spine/Plugin/DescendOrder.pm
==============================================================================
--- spine/branches/spine_2_2-devel/lib/Spine/Plugin/DescendOrder.pm Tue Mar 30 08:35:44 2010 (r317)
+++ spine/branches/spine_2_2-devel/lib/Spine/Plugin/DescendOrder.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -23,116 +23,237 @@
package Spine::Plugin::DescendOrder;
use base qw(Spine::Plugin);
-use Spine::Constants qw(:plugin);
+use Spine::Constants qw(:plugin :keys);
use Spine::Data;
use Spine::Plugin::Interpolate;
+use File::Spec::Functions;
-our ($VERSION, $DESCRIPTION, $MODULE, $ABORT, $CURRENT_DEPTH, $MAX_NESTING_DEPTH);
+our ( $VERSION, $DESCRIPTION, $MODULE, $ABORT, $CURRENT_DEPTH,
+ $MAX_NESTING_DEPTH );
-$VERSION = sprintf("%d", q$Revision$ =~ /(\d+)/);
-$DESCRIPTION = "Determines which policies to apply based on the spine-config" .
- " directory hierarchy layout";
+$VERSION = sprintf( "%d", q$Revision$ =~ /(\d+)/ );
+$DESCRIPTION =
+ "Determines which policies to apply based on the spine-config"
+ . " directory hierarchy layout";
-$MODULE = { author => 'oss...@ticketmaster.com',
+$MODULE = { author => 'oss...@ticketmaster.com',
description => $DESCRIPTION,
- version => $VERSION,
- hooks => { 'DISCOVERY/policy-selection' =>
- [ { name => 'descend',
- code => \&descend_order } ]
- }
- };
+ version => $VERSION,
+ hooks => {
+ "DISCOVERY/populate" => [{ name => 'init_descend_order',
+ code => \&init,
+ provides => ["hierarchy_key"]
+ }, ],
+ "DISCOVERY/policy-selection" => [
+ { name => 'create_descend_order',
+ code => \&create_order,
+ provides => ["hierarchy"] }, ],
+ } };
+
+use constant POLICY_KEY => "policy_hierarchy";
+
+# put the special descend key in place
+sub init {
+ my ($c) = shift;
+ $c->set( SPINE_HIERARCHY_KEY,
+ new Spine::Plugin::DescendOrder::Key( \&resolve, [$c] ) );
+ return PLUGIN_SUCCESS;
+}
-use File::Spec::Functions;
+# load the policy key into the descend key
+sub create_order {
+ my ($c) = @_;
-$ABORT = 0;
-$CURRENT_DEPTH = 0;
-$MAX_NESTING_DEPTH = 15;
+ my $descend_key = $c->getkey(SPINE_HIERARCHY_KEY);
+ my $policy_key = $c->getkey(POLICY_KEY);
+ $descend_key->merge($policy_key);
+ return PLUGIN_SUCCESS;
+}
-sub descend_order
-{
- my $c = shift;
+# This is a callback that gets used each time an item is added to the
+# policy_hirearchy key
+sub resolve {
+ my ( $descend_key, $item, $c ) = @_;
+
+ my $registry = new Spine::Registry;
+ my $point = $registry->get_hook_point('DISCOVERY/Descend/resolve');
+ my ( undef, $rc, undef ) =
+ $point->run_hooks_until( PLUGIN_STOP, $c, $descend_key, $item );
+ return $rc;
+}
- my $policy = $c->getvals('policy_hierarchy');
- unless ($policy) {
- $c->error('no "policy_hierarchy" key defined.', 'crit');
- return PLUGIN_FATAL;
- }
+# The following package is a special implementation of a spine key
+# just for descend items
+#
+# You can think of set/get calls as a way to store standard data
+# merge is magic and actaully puts the data into the chain as well as
+# building dependancies between items
+package Spine::Plugin::DescendOrder::Key;
+use base qw(Spine::Key);
+
+sub new {
+ my $klass = shift;
+
+ # These are used to resolve items as they are added
+ # so we expand a descend item when added
+ my $add_cb = shift;
+ my $add_args = shift;
+
+ my $chain = new Spine::Chain( merge_deps => 1,
+ remove_orphans => 1 );
+
+ my $self = Spine::Key->new();
+
+ $self = bless( $self, $klass );
+
+ # store the call back for later use.
+ $self->metadata_set( "add_callback" => [ $add_cb, $add_args ] );
+ $self->metadata_set( "chain" => $chain );
- # Walk the policy_hierarchy key and build full list by processing
- # any "include" or "config/include" files we find.
- foreach my $dir (@{$policy}) {
- push @{$c->{c_hierarchy}}, get_includes($c, $dir);
- }
+ return $self;
+}
+
+# item is always a hash containing at least name uri and posiably dependencies
+sub _add {
+ my ( $self, $item ) = @_;
+
+ my $chain = $self->metadata("chain");
+
+ if ( exists $item->{dependencies} && defined $item->{dependencies} ) {
+ my $deps = $item->{dependencies};
+
+ # the user might have passed dependencies as single items
+ # outside of array refs if they only have one item i.e succedes => "foo"
+ # rather then succedes => [ "foo" ], lets clean up!
+ foreach ( keys %$deps ) {
+ $deps->{$_} = [ $deps->{$_} ] unless ( ref( $deps->{$_} ) );
+ }
- if ($ABORT)
- {
- return PLUGIN_FATAL;
}
- else
- {
- return PLUGIN_SUCCESS;
+
+ # the item to be added to the chain and protentiall also the dependancies
+ my %to_add = (name => $item->{name},
+ data => $item,
+ %{ exists $item->{dependencies} ? $item->{dependencies} : {} }
+ );
+
+ # We use the uri as the name if no name is given
+ $chain->add(%to_add);
+
+ # FIXME: at this point we do a call to resolv_order
+ # to detect loops, it's not very clean to do this
+ # for every addition so I will REFACTOR this
+ # TODO: log loop errors
+ return undef unless ( $self->resolv_order() );
+
+ # call the add callback with will expand this item
+ # this includes resolving related overlays, config and
+ # child descend location.
+ my $cb_info = $self->metadata("add_callback");
+ &{ $cb_info->[0] }( $self, $item, @{ $cb_info->[1] } );
+}
+
+sub remove {
+ my ( $self, $name ) = @_;
+
+ # If passed the item rather then name we might as well deal with it.
+ # Also since we will add items using the uri as the name if no name is given
+ # deal with that two.
+ if ( ref($name) ) {
+ if ( exists $name->{name} ) {
+ $name = $name->{name};
+ }
+ $name = $name->{uri};
}
+
+ my $chain = $self->metadata("chain");
+ $chain->remove($name);
}
+sub resolv_order {
+ my ($self) = @_;
+ return $self->metadata("chain")->head();
+}
-sub get_includes
-{
- my $c = shift;
- my $directory = shift;
+sub data_getref {
+ my $self = shift;
+ return \[ $self->metadata("chain")->head() ];
+}
- my (@included, $entry);
+# merge in some new branches, parent can relate to either
+# the parent item or name or undef if it's a root item
+sub merge {
+ my ( $self, $item, $parent ) = @_;
- # FIXME Too deep. Log something.
- if (++$CURRENT_DEPTH > $MAX_NESTING_DEPTH) {
- $c->error('Search depth limit exceeded, aborting!', 'crit');
- $ABORT = 1;
- goto empty_set;
- }
+ my $deps = undef;
- # If we want includes to appear before their children do so now.
- if ($c->getval_last('include_ordering') eq 'post') {
- push @included, $directory;
+ if ( $self->is_related($item) ) {
+ $item = $item->merge_helper($self);
}
- foreach my $path (qw(include config/include)) {
- my $inc_file = catfile($directory, $path);
+ # Recursive call if we have more then one item
+ if ( ref($item) eq "ARRAY" ) {
+ foreach (@$item) {
+ $self->merge( $_, $parent );
- # An empty set is perfectly acceptable
- unless (-f $inc_file) {
- next;
}
+ return undef;
+ }
- my $includes = $c->read_keyfile($inc_file);
+ if ( ref($item) eq "HASH" ) {
- unless (defined($includes)) {
- next;
+ # if it's a hash make sure it has a name not just a uri
+ # we use the uri as the name if missing.
+ unless ( exists $item->{name} ) {
+ $item->{name} = $item->{uri};
}
+ } else {
- foreach my $entry (@{$includes})
- {
- $c->print(3, "including $entry");
- my $inc_dir = catfile($c->getval_last('include_dir'), $entry);
- $c->print(3, "including $inc_dir");
-
- # If it looks like an absolute path, assume that it's from the top
- # of the configuration root.
- if ($entry =~ m#^/#) {
- $inc_dir = catfile($c->{c_croot}, $entry);
- }
- push @included, @{get_includes($c, $inc_dir)};
- }
+ # if it's just a scalar then we expect that the item is the uri
+ $item = { name => $item,
+ uri => $item, };
}
- # If we want includes to appear after their children (default) do so now.
- if ($c->getval_last('include_ordering') ne 'post') {
- push @included, $directory;
+ if ( defined $parent ) {
+
+ # make sure it's the name not the item it self
+ if ( ref($parent) ) {
+ $parent = $parent->{name};
+ }
+
+ # add it as a dependency
+ $item->{dependencies} = {} unless exists( $item->{dependencies} );
+ $item->{dependencies}->{succedes} = []
+ unless exists( $item->{dependencies}->{succedes} );
+ push @{ $item->{dependencies}->{succedes} }, $parent;
}
- --$CURRENT_DEPTH;
- empty_set:
- return wantarray ? @included : \@included;
+ # add the item to the key
+ $self->_add($item);
+
+ # finally blank anything pending within data
+ # all data is used for is pending stuff to merge anyhow
+ $self->clear();
}
+# I don't think you would ever use this but it's here in case....
+sub replace {
+ my $self = shift;
+
+ #blank out the chain
+ my $chain = new Spine::Chain( merge_deps => 1,
+ remove_orphans => 1 );
+
+ $self->metadata_set( "chain", $chain );
+
+ # add in the replacement data
+ $self->merge(@_);
+}
+
+# This is used to tell operators that this key should be merged by default
+sub merge_default() {
+ return 1;
+}
1;
Modified: spine/branches/spine_2_2-devel/lib/Spine/Plugin/Overlay.pm
==============================================================================
--- spine/branches/spine_2_2-devel/lib/Spine/Plugin/Overlay.pm Tue Mar 30 08:35:44 2010 (r317)
+++ spine/branches/spine_2_2-devel/lib/Spine/Plugin/Overlay.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -1,4 +1,3 @@
-# -*- mode: perl; cperl-continued-brace-offset: -4; indent-tabs-mode: nil; -*-
# vim:shiftwidth=2:tabstop=8:expandtab:textwidth=78:softtabstop=4:ai:
# $Id$
@@ -23,249 +22,242 @@
package Spine::Plugin::Overlay;
use base qw(Spine::Plugin);
-use Spine::Constants qw(:plugin);
-
-our ($VERSION, $DESCRIPTION, $MODULE, $DONTDELETE, $TMPDIR, @ENTRIES);
-
-$VERSION = sprintf("%d", q$Revision$ =~ /(\d+)/);
-$DESCRIPTION = "Plugin for creating and processing overlays.";
-
-$MODULE = { author => 'oss...@ticketmaster.com',
- description => $DESCRIPTION,
- version => $VERSION,
- hooks => { PREPARE => [ { name => 'build_overlay',
- code => \&build_overlay } ],
- APPLY => [ { name => 'apply_overlay',
- code => \&apply_overlay } ],
- CLEAN => [ { name => 'clean_overlay',
- code => \&clean_overlay } ]
- },
- cmdline => { options => { 'keep-overlay' => \$DONTDELETE } }
- };
-
+use Spine::Constants qw(:plugin :basic :keys);
+use Spine::Registry;
+use IO::File;
use Digest::MD5;
use File::Find;
+use Text::Diff;
use File::Spec::Functions;
use File::stat;
use Fcntl qw(:mode);
-use IO::File;
-use Spine::Constants qw(:basic);
use Spine::Util qw(simple_exec do_rsync mkdir_p octal_conv uid_conv gid_conv);
-use Text::Diff;
+our ( $VERSION, $DESCRIPTION, $MODULE, $DONTDELETE, $TMPDIR, @ENTRIES );
+
+$VERSION = sprintf( "%d", q$Revision$ =~ /(\d+)/ );
+
+$DESCRIPTION = "Parselet::Basic, processes Basic keys";
+
+$MODULE = {
+ author => 'oss...@ticketmaster.com',
+ description => $DESCRIPTION,
+ version => $VERSION,
+ hooks => {
+ "DISCOVERY/populate" => [ { name => 'create_overlay_store',
+ code => \&init } ],
+
+ PREPARE => [ { name => 'build_overlay',
+ code => \&build_overlay } ],
+ APPLY => [ { name => 'apply_overlay',
+ code => \&apply_overlay } ],
+ CLEAN => [ { name => 'clean_overlay',
+ code => \&clean_overlay } ] },
+ cmdline => { options => { 'keep-overlay' => \$DONTDELETE } } };
+
+# FIXME: icky
+my $c;
my $DRYRUN;
-my $c;
-sub build_overlay
-{
- #
- # I can't for the life of me recall what I was going to use this for.
- #
- # rtilder Tue Dec 19 08:38:10 PST 2006
- #
- my $self = __PACKAGE__->new();
+sub init {
+ my $c = shift;
+ $c->set( SPINE_OVERLAY_KEY, new Spine::Plugin::Overlay::Key() );
+ return PLUGIN_SUCCESS;
+}
+
+sub load_overlays {
+ my ($c) = @_;
+
+ my $registry = new Spine::Registry;
+
+ my $point = $registry->get_hook_point('PREPARE/Overlay/load');
+ foreach my $branch ( @{ $c->getvals("c_hierarchy") } ) {
+ my $rc = $point->run_hooks( $c, $branch );
+ return PLUGIN_ERROR unless ( $rc == 0 );
+ }
+ return PLUGIN_SUCCESS;
+}
+sub build_overlay {
$c = shift;
- my $croot = $c->getval('c_croot');
- my $tmpdir = $c->getval('c_tmpdir');
+
+ # Will load the overlays based on descend information
+ # TODO: make this nice, also should use spine_success rather then plugin
+ return PLUGIN_ERROR
+ unless ( load_overlays($c) == PLUGIN_SUCCESS );
+
+ my $croot = $c->getval('c_croot');
+ my $tmpdir = $c->getval('c_tmpdir');
my $tmplink = $c->getval('c_tmplink');
- my @excludes = @{$c->getvals('build_overlay_excludes')}
- if ($c->getval('build_overlay_excludes'));
- my $masochist = $c->getval_last('masochistic_build_overlay');
- my $rval = 0;
- my $youve_been_warned = 0;
- $DRYRUN = $c->getval('c_dryrun');
+ my $overlay_data = $c->getkey(SPINE_OVERLAY_KEY);
+ my $bound_overlays = $overlay_data->get_bound();
+
+ my @excludes = @{ $c->getvals('build_overlay_excludes') }
+ if ( $c->getval('build_overlay_excludes') );
+ my $ignore_errs = $c->getval_last('ignore_errors_build_overlay');
+
+ my $dryrun = $c->getval('c_dryrun');
+
+ my $registry = new Spine::Registry;
+ my $point = $registry->get_hook_point('PREPARE/Overlay/build');
+
+ my $settings = { tmpdir => $tmpdir,
+ excludes => \@excludes,
+ croot => $croot,
+ dryrun => $dryrun };
+
+ # Initial steps, should be moved to a hook near the start....
+ remove_tmpdir( $c, $tmpdir );
+ unless ( mkdir_p( $tmpdir, 0755 ) ) {
- remove_tmpdir($c, $tmpdir);
- unless (mkdir_p($tmpdir, 0755))
- {
# Return a fatal error to the caller.
- $c->error("could not create temp directory", 'crit');
+ $c->error( "could not create temp directory", 'crit' );
return PLUGIN_FATAL;
}
# Create a symlink pointing to the most recent overlay.
- unlink $tmplink if (-l $tmplink);
+ unlink $tmplink if ( -l $tmplink );
symlink $tmpdir, $tmplink;
- my $descend_order = $c->getvals("c_descend_order");
- unless ($descend_order) {
- $c->error("nothing in the descend order so no overlays to process",
- 'warn');
- return PLUGIN_SUCCESS;
- }
-
- # This is a for loop instead of a foreach because I want to manipulate the
- # $dir variable without affecting the Spine::Data object's data members
- #
- # rtilder Tue Dec 19 14:11:38 PST 2006
- #
- for my $dir ( @{$descend_order})
- {
- my @overlay_map = ('overlay:/');
- if ( exists $c->{'overlay_map'} )
- {
- @overlay_map = @{$c->getvals("overlay_map")};
- }
- for my $element ( @overlay_map )
- {
- my ($overlay, $target) = split( /:/, $element);
- $overlay = "${dir}/${overlay}/";
-
- unless (file_name_is_absolute($dir)) {
- $overlay = catfile($croot, $overlay);
- $overlay .= '/'; # catfile() removes trailing slashes
- }
-
- if ( -d $overlay )
- {
- $c->print(4, "performing overlay from $dir");
- unless (do_rsync(Config => $c,
- Inert => 1,
- Source => $overlay,
- Target => catfile($tmpdir, $target),
- Excludes => \@excludes)) {
- $rval++;
- }
- }
+ # loop over all the overlays that have neem bound to paths
+ foreach my $overlay_item ( @{$bound_overlays} ) {
- # If we've had errors, then we should quit.
- if ($rval) {
- unless ($masochist) {
- return PLUGIN_FATAL;
- }
+ # create settings for this overlay from the global and overlay items
+ my $item_settings = { %$settings, %$overlay_item };
- unless ($youve_been_warned) {
- $youve_been_warned++;
- $c->print(1, "You're an idiot because you have the ",
- 'masochistic_build_overlay key set to something ',
- 'other than 0.');
- $c->print(1, "Prepare for PAIN!");
- }
- }
+ # The following two gets set so that things like templates within
+ # overlays know where they came from
+ $c->set( "c_current_overlay", $item_settings );
+ $c->print( 4,
+ "Processing the overlay (" . $item_settings->{uri} . ")" );
+ my ( undef, $rc, undef ) =
+ $point->run_hooks_until( PLUGIN_STOP, $c, $item_settings );
+
+ if ( $rc == PLUGIN_NOHOOKS ) {
+ $c->cprint( "unable to build overlay ($overlay_item->{uri})."
+ . " No plugins for PREPARE/Overlay/build",
+ 2 );
+ return PLUGIN_NOHOOKS;
+ }
+
+ if ($rc == PLUGIN_FATAL ) {
+ $c->error( "could not build overlay \""
+ . $overlay_item->{uri}
+ . "\" for ("
+ . $overlay_item->{path} . ".)",
+ $ignore_errs ? 'warn' : 'error' );
+ return PLUGIN_FATAL unless $ignore_errs;
}
}
-
- # If $rval isn't 0 then we've had errors and we should return PLUGIN_ERROR
- #
- # This should always return PLUGIN_SUCCESS because any errors should be
- # caught earlier on.
- return $rval ? PLUGIN_ERROR : PLUGIN_SUCCESS;
+ return PLUGIN_SUCCESS;
}
-
-sub apply_overlay
-{
+sub apply_overlay {
$c = shift;
- my $tmpdir = $c->getval('c_tmpdir');
- my $tmplink = $c->getval('c_tmplink');
- my $overlay_root = $c->getval('overlay_root');
- my $excludes = $c->getvals('apply_overlay_excludes');
+ my $tmpdir = $c->getval('c_tmpdir');
+ my $tmplink = $c->getval('c_tmplink');
+ my $overlay_root = $c->getval('overlay_root');
+ my $excludes = $c->getvals('apply_overlay_excludes');
my $max_diff_lines = $c->getval('max_diff_lines_to_print');
- my %rsync_args = (Config => $c, Source => $tmpdir, Output => undef,
- Target => $overlay_root, Options => [qw(-c)],
- Excludes => $excludes);
+ my %rsync_args = ( Config => $c,
+ Source => $tmpdir,
+ Output => undef,
+ Target => $overlay_root,
+ Options => [qw(-c)],
+ Excludes => $excludes );
- $TMPDIR = $tmpdir;
- $DRYRUN = $c->getval('c_dryrun');
+ $TMPDIR = $tmpdir;
+ $DRYRUN = $c->getval('c_dryrun');
@ENTRIES = ();
- if ($overlay_root eq '')
- {
- $c->error('overlay_root key does not exist, no changes will be '
- . 'applied!', 'warn');
+ if ( $overlay_root eq '' ) {
+ $c->error( 'overlay_root key does not exist, no changes will be '
+ . 'applied!',
+ 'warn' );
}
- unless (-d $tmpdir)
- {
- $c->error("temp directory [$tmpdir] does not exist", 'crit');
- return PLUGIN_FATAL;
+ unless ( -d $tmpdir ) {
+ $c->error( "temp directory [$tmpdir] does not exist", 'crit' );
+ return PLUGIN_FATAL;
}
# Make sure that the source matches rsync's include pattern rules.
- unless ($tmpdir =~ m|/$|) {
+ unless ( $tmpdir =~ m|/$| ) {
$rsync_args{Source} = "$tmpdir/";
}
# Populates @ENTRIES via the find_changed() callback
- find( { follow => 0, no_chdir => 1, wanted => \&find_changed },
- $tmpdir);
+ find( { follow => 0, no_chdir => 1, wanted => \&find_changed }, $tmpdir );
- foreach my $srcfile (@ENTRIES)
- {
- chomp $srcfile; # most likely redundant
+ foreach my $srcfile (@ENTRIES) {
+ chomp $srcfile; # most likely redundant
my $destfile = $srcfile;
- if ($srcfile !~ m!^$tmpdir!)
- {
+ if ( $srcfile !~ m!^$tmpdir! ) {
$srcfile = $tmpdir . $srcfile;
- }
- else
- {
- (undef, $destfile) = split(/$tmpdir/, $srcfile);
+ } else {
+ ( undef, $destfile ) = split( /$tmpdir/, $srcfile );
}
next if $destfile =~ /^$/;
- sync_attribs($c, $srcfile, $destfile) if (-e $destfile);
+ sync_attribs( $c, $srcfile, $destfile ) if ( -e $destfile );
# Compare the source and destination files. If nothing
# has changed, delete the source file from the temp overlay.
- if ( (-f $destfile) && (-f $srcfile) )
- {
+ if ( ( -f $destfile ) && ( -f $srcfile ) ) {
+
# if the source/dest file is binary, don't bother calculating
# diffs, since they are likely undisplayable anyways
- if ( (-B $destfile && ! -z $destfile)
- || (-B $srcfile && ! -z $srcfile) )
+ if ( ( -B $destfile && !-z $destfile )
+ || ( -B $srcfile && !-z $srcfile ) )
{
- $c->print(2, "Patching binary file $destfile");
+ $c->print( 2, "Patching binary file $destfile" );
next;
}
- my $diff = diff($destfile, $srcfile);
- my @diff = split(/\n/, $diff);
+ my $diff = diff( $destfile, $srcfile );
+ my @diff = split( /\n/, $diff );
- if (length($diff))
- {
- $c->print(2, "updating $destfile");
+ if ( length($diff) ) {
+ $c->print( 2, "updating $destfile" );
my $size = scalar(@diff);
- if (defined($max_diff_lines)
- and $max_diff_lines > 0
- and $size >= $max_diff_lines)
+ if ( defined($max_diff_lines)
+ and $max_diff_lines > 0
+ and $size >= $max_diff_lines )
{
- $c->print(2, "Changes to $destfile are too large to print("
- . "$size >= $max_diff_lines lines)");
- }
- else
- {
- foreach my $line (@diff)
- {
+ $c->print( 2,
+ "Changes to $destfile are too large to print("
+ . "$size >= $max_diff_lines lines)" );
+ } else {
+ foreach my $line (@diff) {
$line =~ s/$tmpdir/spine-overlay/g;
- $c->cprint(" $line", 2, 0) if ($line =~ /^[+-]/);
+ $c->cprint( " $line", 2, 0 )
+ if ( $line =~ /^[+-]/ );
}
}
- utime(time, time, $srcfile);
+ utime( time, time, $srcfile );
}
- }
- elsif (not -e $destfile)
- {
+ } elsif ( not -e $destfile ) {
my $src_stat = lstat($srcfile);
- $c->print(2, "creating $destfile"
- . ' [mode ' . octal_conv($src_stat->mode) . ' |'
- . ' owner/group ' . uid_conv($src_stat->uid) . ':'
- . gid_conv($src_stat->gid) . ']');
+ $c->print( 2,
+ "creating $destfile"
+ . ' [mode '
+ . octal_conv( $src_stat->mode ) . ' |'
+ . ' owner/group '
+ . uid_conv( $src_stat->uid ) . ':'
+ . gid_conv( $src_stat->gid ) . ']' );
- utime(time, time, $srcfile);
+ utime( time, time, $srcfile );
}
}
- unless ($DRYRUN)
- {
- if (do_rsync(%rsync_args) != SPINE_SUCCESS)
- {
+ unless ($DRYRUN) {
+ if ( do_rsync(%rsync_args) != SPINE_SUCCESS ) {
+
# These particular return values don't necessarily indicate that
# the transfer wasn't at least partially successful. See the
# rsync man page for more information. Necessary because /media
@@ -275,19 +267,17 @@
# rtilder Wed Jun 6 14:08:49 PDT 2007
my $rc = $? >> 8;
- if ($rc == 23 or $rc == 24)
- {
- $c->error("rsync partial failure! This is probably very bad.",
- 'err');
+ if ( $rc == 23 or $rc == 24 ) {
+ $c->error( "rsync partial failure! This is probably very bad.",
+ 'err' );
- if ($c->getval('partial_rsync_ok'))
- {
+ if ( $c->getval('partial_rsync_ok') ) {
return PLUGIN_SUCCESS;
}
}
- $c->error("rsync failed! Couldn't apply filesystem changes",
- 'err');
+ $c->error( "rsync failed! Couldn't apply filesystem changes",
+ 'err' );
return PLUGIN_FATAL;
}
}
@@ -295,91 +285,17 @@
return PLUGIN_SUCCESS;
}
-
-sub sync_attribs
-{
- $c = shift;
- my ($srcfile, $destfile) = @_;
-
- my $src_stat = lstat($srcfile);
- my $dest_stat = lstat($destfile);
-
- unless ($src_stat->mode eq $dest_stat->mode)
- {
- $c->print(2, "updating permissions on $destfile from " .
- octal_conv($dest_stat->mode) . " to " .
- octal_conv($src_stat->mode));
-
- chmod $src_stat->mode, $destfile
- unless ($DRYRUN);
- }
-
- unless ( ($src_stat->uid eq $dest_stat->uid) and
- ($src_stat->gid eq $dest_stat->gid) )
- {
- $c->print(2, "updating owner/group on $destfile from " .
- uid_conv($dest_stat->uid) . ":" .
- gid_conv($dest_stat->gid) . " to " .
- uid_conv($src_stat->uid) . ":" .
- gid_conv($src_stat->gid));
- }
-
- chown $src_stat->uid, $src_stat->gid, $destfile
- unless ($DRYRUN);
-}
-
-
-sub clean_overlay
-{
- $c = shift;
- my $tmpdir = $c->getval('c_tmpdir');
- my $tmplink = $c->getval('c_tmplink');
- my $rc = 0;
-
- # If --keep-overlay was specified on the command line, pretend like we did
- if ($DONTDELETE) {
- $c->print(1, "Leaving overlay in \"$tmpdir\"");
- return PLUGIN_SUCCESS;
- }
-
- $rc = remove_tmpdir($c, $tmpdir);
- unlink $tmplink;
-
- return PLUGIN_SUCCESS;
-}
-
-
-sub remove_tmpdir
-{
- my $c = shift;
- my $tmpdir = shift;
-
- # rm -rf paranoia.
- if ( ($tmpdir =~ m@^/tmp/.*@) and ($tmpdir =~ m@[^\.~]*@) )
- {
- my $result = simple_exec(merge_error => 1,
- exec => 'rm',
- inert => 1,
- c => $c,
- args => "-rf $tmpdir");;
- return 1;
- }
-
- return 0;
-}
-
-
#
# @ENTRIES is a module level global
#
-sub find_changed
-{
+sub find_changed {
my $fname = $File::Find::name;
+
# The target filename
- my (undef, $dest) = split(/$TMPDIR/, $fname);
+ my ( undef, $dest ) = split( /$TMPDIR/, $fname );
- if ($fname eq $TMPDIR
- or $fname eq "$TMPDIR/")
+ if ( $fname eq $TMPDIR
+ or $fname eq "$TMPDIR/" )
{
return;
}
@@ -388,45 +304,36 @@
my $dstat = lstat($dest);
# Destination doesn't exist?
- unless (defined($dstat))
- {
+ unless ( defined($dstat) ) {
goto changed;
}
- #
- # HAHA! I'm evil! unless ( ) { ... } elsif ( ) { ... } else { ... }
- #
- # rtilder Tue May 1 11:55:41 PDT 2007
- #
-
# rdev seems unreliable on 2.4 kernels
my $kernel_version = $c->getval('c_current_kernel_version');
- my $rdev = 0;
- if ($kernel_version =~ /^2\.4\./)
- {
+ my $rdev = 0;
+ if ( $kernel_version =~ /^2\.4\./ ) {
+ $rdev = 1;
+ } elsif ( $lstat->rdev == $dstat->rdev ) {
$rdev = 1;
- }
- elsif ($lstat->rdev == $dstat->rdev)
- {
- $rdev = 1;
}
# Any change in the important stat data? We disregard A, C, and M times
# for the newly created overlay for obvious reasons
- unless ($lstat->mode == $dstat->mode
- and $lstat->uid == $dstat->uid
- and $lstat->gid == $dstat->gid
- and $lstat->size == $dstat->size
- and $rdev)
+ unless ( $lstat->mode == $dstat->mode
+ and $lstat->uid == $dstat->uid
+ and $lstat->gid == $dstat->gid
+ and $lstat->size == $dstat->size
+ and $rdev )
{
goto changed;
}
+
# If it's a file, are the contents the same?
- elsif (S_ISREG($lstat->mode) and S_ISREG($dstat->mode)) {
+ elsif ( S_ISREG( $lstat->mode ) and S_ISREG( $dstat->mode ) ) {
my $currfh = new IO::File($dest);
my $newfh = new IO::File($fname);
- my $curr = new Digest::MD5();
- my $new = new Digest::MD5();
+ my $curr = new Digest::MD5();
+ my $new = new Digest::MD5();
binmode $currfh;
binmode $newfh;
@@ -439,20 +346,19 @@
undef $currfh;
undef $newfh;
- unless ($curr->hexdigest() eq $new->hexdigest())
- {
+ unless ( $curr->hexdigest() eq $new->hexdigest() ) {
goto changed;
}
undef $curr;
undef $new;
- }
- elsif (S_ISLNK($lstat->mode) and S_ISLNK($dstat->mode)) {
+ } elsif ( S_ISLNK( $lstat->mode ) and S_ISLNK( $dstat->mode ) ) {
my $curr_target = readlink $fname;
- my $new_target = readlink $dest;
+ my $new_target = readlink $dest;
- if ((defined($curr_target) and defined($new_target))
- and $curr_target ne $new_target) {
+ if ( ( defined($curr_target) and defined($new_target) )
+ and $curr_target ne $new_target )
+ {
goto changed;
}
}
@@ -465,5 +371,239 @@
return;
}
+sub clean_overlay {
+ my $c = shift;
+ my $tmpdir = $c->getval('c_tmpdir');
+ my $tmplink = $c->getval('c_tmplink');
+ my $rc = 0;
+
+ # If --keep-overlay was specified on the command line, pretend like we did
+ if ($DONTDELETE) {
+ $c->print( 1, "Leaving overlay in \"$tmpdir\"" );
+ return PLUGIN_SUCCESS;
+ }
+
+ $rc = remove_tmpdir( $c, $tmpdir );
+ unlink $tmplink;
+
+ return PLUGIN_SUCCESS;
+}
+
+sub remove_tmpdir {
+ my $c = shift;
+ my $tmpdir = shift;
+
+ # rm -rf paranoia.
+ if ( ( $tmpdir =~ m@^/tmp/.*@ ) and ( $tmpdir =~ m@[^\.~]*@ ) ) {
+ my $result = simple_exec( merge_error => 1,
+ exec => 'rm',
+ inert => 1,
+ c => $c,
+ args => "-rf $tmpdir" );
+ return 1;
+ }
+
+ return 0;
+}
+
+sub sync_attribs {
+ $c = shift;
+ my ( $srcfile, $destfile ) = @_;
+
+ my $src_stat = lstat($srcfile);
+ my $dest_stat = lstat($destfile);
+
+ unless ( $src_stat->mode eq $dest_stat->mode ) {
+ $c->print( 2,
+ "updating permissions on $destfile from "
+ . octal_conv( $dest_stat->mode ) . " to "
+ . octal_conv( $src_stat->mode ) );
+
+ chmod $src_stat->mode, $destfile
+ unless ($DRYRUN);
+ }
+
+ unless ( ( $src_stat->uid eq $dest_stat->uid )
+ and ( $src_stat->gid eq $dest_stat->gid ) )
+ {
+ $c->print( 2,
+ "updating owner/group on $destfile from "
+ . uid_conv( $dest_stat->uid ) . ":"
+ . gid_conv( $dest_stat->gid ) . " to "
+ . uid_conv( $src_stat->uid ) . ":"
+ . gid_conv( $src_stat->gid ) );
+ }
+
+ chown $src_stat->uid, $src_stat->gid, $destfile
+ unless ($DRYRUN);
+}
+
+1;
+
+# The following package is a special implementation of a spine key
+# just for overlays
+package Spine::Plugin::Overlay::Key;
+use base qw(Spine::Key);
+
+sub new {
+ my $klass = shift;
+
+ my $data = { bound => [],
+ overlays => {},
+ overlay_data => {} };
+
+ my $self = Spine::Key->new($data);
+ return bless( $self, $klass );
+}
+
+sub add {
+ my ( $self, $uri, $name ) = @_;
+
+ my $data = $self->get();
+
+ # We use the uri as the name if no name is given
+ $data->{overlays}->{ defined $name ? $name : $uri } = $uri;
+}
+
+sub add_data {
+ my ( $self, $name, $data ) = @_;
+
+ $data = $self->get();
+
+ $data->{overlays}->{$name} = $data;
+
+}
+
+sub remove {
+ my ( $self, $name ) = @_;
+
+ my $data = $self->get();
+
+ if ( exists $data->{overlays}->{$name} ) {
+ delete $data->{overlays}->{$name};
+ }
+
+ if ( exists $data->{overlay_data}->{$name} ) {
+ delete $data->{overlay_data}->{$name};
+ }
+
+ # remove any time this was bound
+ for ( my $i = 0 ; $i < scalar( @{ $data->{bound} } ) ; $i++ ) {
+ if ( $data->{bound}->[$i]->[0] eq $name ) {
+ splice( @{ $data->{bound} }, $i, 1 );
+ }
+ }
+}
+
+sub bind {
+ my ( $self, $name, $path ) = @_;
+
+ # Deal with arrays as if it's multiple paths
+ if ( ref($path) eq "ARRAY" ) {
+ foreach (@$path) {
+ $self->bind( $name, $_ );
+ }
+ return undef;
+ }
+
+ my $data = $self->get();
+
+ return undef unless ( exists $data->{overlays}->{$name} );
+
+ push @{ $data->{bound} }, [ $name, $path ];
+
+ return $name;
+}
+
+# TODO
+sub unbind {
+ my ( $self, $name, $path ) = @_;
+}
+
+sub data_getref {
+ my $self = shift;
+
+ return \$self->get_bound(@_);
+}
+
+sub get_bound {
+ my ($self) = @_;
+
+ my $data = $self->get();
+ my $items = [];
+
+ # return an array of arrays containgin name, path, uri
+ foreach my $bound ( @{ $data->{bound} } ) {
+ my $overlay_data =
+ exists $data->{overlay_data}->{ $bound->[0] }
+ ? $data->{overlay_data}->{ $bound->[0] }
+ : "";
+ my $uri = $data->{overlays}->{ $bound->[0] };
+ push @$items,
+ { name => $bound->[0],
+ path => $bound->[1],
+ uri => $uri,
+ data => $overlay_data };
+ }
+ return $items;
+}
+
+# override the standard merge function
+sub merge {
+ my ( $self, $obj ) = @_;
+
+ if ( $self->is_related($obj) ) {
+ $obj = $obj->merge_helper($obj);
+ }
+
+ # Recursive call if we have more then one item
+ if ( ref($obj) eq "ARRAY" ) {
+ foreach (@$obj) {
+ $self->merge($_);
+
+ }
+ return undef;
+ }
+
+ if ( ref($obj) eq "HASH" ) {
+
+ my $name;
+ if ( exists $obj->{name} ) {
+ $name = $obj->{name};
+ } elsif ( exists $obj->{uri} ) {
+ $name = $obj->{uri};
+ } else {
+
+ #TODO: report error somehow????
+ return undef;
+ }
+
+ # Add it in if the uri was given
+ if ( exists $obj->{uri} ) {
+ $self->add( $obj->{uri}, $name );
+ }
+
+ # Register any data if there is any
+ if ( exists $obj->{data} ) {
+ $self->add_data( $name, $obj->{data} );
+ }
+
+ # bind it is there are any bind options
+ if ( exists $obj->{bind} ) {
+ $self->bind( $name, $obj->{bind} );
+ }
+
+ } else {
+
+ # If it's not a ref then we assume it's a uri to be bound to '/'
+ $self->add( $obj, $obj );
+ $self->bind( $obj, "/" );
+ }
+}
+
+# This is used to tell operators that this key should be merged by default
+sub merge_default() {
+ return 1;
+}
1;
Copied and modified: spine/branches/spine_2_2-devel/lib/Spine/Plugin/Overlay/Disk.pm (from r304, spine/branches/spine_2_2-devel/lib/Spine/Plugin/Overlay.pm)
==============================================================================
--- spine/branches/spine_2_2-devel/lib/Spine/Plugin/Overlay.pm Fri Nov 20 06:12:16 2009 (r304, copy source)
+++ spine/branches/spine_2_2-devel/lib/Spine/Plugin/Overlay/Disk.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -21,449 +21,111 @@
use strict;
-package Spine::Plugin::Overlay;
+package Spine::Plugin::Overlay::Disk;
use base qw(Spine::Plugin);
-use Spine::Constants qw(:plugin);
+use Spine::Constants qw(:plugin :keys);
+use File::Spec::Functions;
+use Spine::Util qw(simple_exec do_rsync mkdir_p octal_conv uid_conv gid_conv);
-our ($VERSION, $DESCRIPTION, $MODULE, $DONTDELETE, $TMPDIR, @ENTRIES);
+our ( $VERSION, $DESCRIPTION, $MODULE, $DONTDELETE, $TMPDIR, @ENTRIES );
-$VERSION = sprintf("%d", q$Revision$ =~ /(\d+)/);
-$DESCRIPTION = "Plugin for creating and processing overlays.";
+$VERSION = sprintf( "%d", q$Revision$ =~ /(\d+)/ );
+$DESCRIPTION = "Disk based overlays.";
-$MODULE = { author => 'oss...@ticketmaster.com',
+$MODULE = { author => 'oss...@ticketmaster.com',
description => $DESCRIPTION,
- version => $VERSION,
- hooks => { PREPARE => [ { name => 'build_overlay',
- code => \&build_overlay } ],
- APPLY => [ { name => 'apply_overlay',
- code => \&apply_overlay } ],
- CLEAN => [ { name => 'clean_overlay',
- code => \&clean_overlay } ]
- },
- cmdline => { options => { 'keep-overlay' => \$DONTDELETE } }
+ version => $VERSION,
+ hooks => {
+ 'PREPARE/Overlay/build' => [
+ { name => 'build_disk_overlay',
+ code => \&build_disk_overlay }
+ ],
+ "PREPARE/Overlay/load" => [
+ { name => "load_descent_overlays",
+ code => \&descent_overlays } ], }
};
-use Digest::MD5;
-use File::Find;
-use File::Spec::Functions;
-use File::stat;
-use Fcntl qw(:mode);
-use IO::File;
use Spine::Constants qw(:basic);
-use Spine::Util qw(simple_exec do_rsync mkdir_p octal_conv uid_conv gid_conv);
-use Text::Diff;
-
-my $DRYRUN;
-my $c;
-
-sub build_overlay
-{
- #
- # I can't for the life of me recall what I was going to use this for.
- #
- # rtilder Tue Dec 19 08:38:10 PST 2006
- #
- my $self = __PACKAGE__->new();
-
- $c = shift;
- my $croot = $c->getval('c_croot');
- my $tmpdir = $c->getval('c_tmpdir');
- my $tmplink = $c->getval('c_tmplink');
- my @excludes = @{$c->getvals('build_overlay_excludes')}
- if ($c->getval('build_overlay_excludes'));
- my $masochist = $c->getval_last('masochistic_build_overlay');
- my $rval = 0;
- my $youve_been_warned = 0;
-
- $DRYRUN = $c->getval('c_dryrun');
-
- remove_tmpdir($c, $tmpdir);
- unless (mkdir_p($tmpdir, 0755))
- {
- # Return a fatal error to the caller.
- $c->error("could not create temp directory", 'crit');
- return PLUGIN_FATAL;
- }
-
- # Create a symlink pointing to the most recent overlay.
- unlink $tmplink if (-l $tmplink);
- symlink $tmpdir, $tmplink;
-
- my $descend_order = $c->getvals("c_descend_order");
- unless ($descend_order) {
- $c->error("nothing in the descend order so no overlays to process",
- 'warn');
- return PLUGIN_SUCCESS;
- }
-
- # This is a for loop instead of a foreach because I want to manipulate the
- # $dir variable without affecting the Spine::Data object's data members
- #
- # rtilder Tue Dec 19 14:11:38 PST 2006
- #
- for my $dir ( @{$descend_order})
- {
- my @overlay_map = ('overlay:/');
- if ( exists $c->{'overlay_map'} )
- {
- @overlay_map = @{$c->getvals("overlay_map")};
- }
- for my $element ( @overlay_map )
- {
- my ($overlay, $target) = split( /:/, $element);
- $overlay = "${dir}/${overlay}/";
-
- unless (file_name_is_absolute($dir)) {
- $overlay = catfile($croot, $overlay);
- $overlay .= '/'; # catfile() removes trailing slashes
- }
-
- if ( -d $overlay )
- {
- $c->print(4, "performing overlay from $dir");
- unless (do_rsync(Config => $c,
- Inert => 1,
- Source => $overlay,
- Target => catfile($tmpdir, $target),
- Excludes => \@excludes)) {
- $rval++;
- }
- }
-
- # If we've had errors, then we should quit.
- if ($rval) {
- unless ($masochist) {
- return PLUGIN_FATAL;
- }
-
- unless ($youve_been_warned) {
- $youve_been_warned++;
- $c->print(1, "You're an idiot because you have the ",
- 'masochistic_build_overlay key set to something ',
- 'other than 0.');
- $c->print(1, "Prepare for PAIN!");
- }
- }
- }
- }
-
- # If $rval isn't 0 then we've had errors and we should return PLUGIN_ERROR
- #
- # This should always return PLUGIN_SUCCESS because any errors should be
- # caught earlier on.
- return $rval ? PLUGIN_ERROR : PLUGIN_SUCCESS;
-}
-
-
-sub apply_overlay
-{
- $c = shift;
- my $tmpdir = $c->getval('c_tmpdir');
- my $tmplink = $c->getval('c_tmplink');
- my $overlay_root = $c->getval('overlay_root');
- my $excludes = $c->getvals('apply_overlay_excludes');
- my $max_diff_lines = $c->getval('max_diff_lines_to_print');
- my %rsync_args = (Config => $c, Source => $tmpdir, Output => undef,
- Target => $overlay_root, Options => [qw(-c)],
- Excludes => $excludes);
-
- $TMPDIR = $tmpdir;
- $DRYRUN = $c->getval('c_dryrun');
- @ENTRIES = ();
-
- if ($overlay_root eq '')
- {
- $c->error('overlay_root key does not exist, no changes will be '
- . 'applied!', 'warn');
- }
-
- unless (-d $tmpdir)
- {
- $c->error("temp directory [$tmpdir] does not exist", 'crit');
- return PLUGIN_FATAL;
- }
-
- # Make sure that the source matches rsync's include pattern rules.
- unless ($tmpdir =~ m|/$|) {
- $rsync_args{Source} = "$tmpdir/";
- }
-
- # Populates @ENTRIES via the find_changed() callback
- find( { follow => 0, no_chdir => 1, wanted => \&find_changed },
- $tmpdir);
-
- foreach my $srcfile (@ENTRIES)
- {
- chomp $srcfile; # most likely redundant
- my $destfile = $srcfile;
-
- if ($srcfile !~ m!^$tmpdir!)
- {
- $srcfile = $tmpdir . $srcfile;
- }
- else
- {
- (undef, $destfile) = split(/$tmpdir/, $srcfile);
- }
- next if $destfile =~ /^$/;
-
- sync_attribs($c, $srcfile, $destfile) if (-e $destfile);
-
- # Compare the source and destination files. If nothing
- # has changed, delete the source file from the temp overlay.
- if ( (-f $destfile) && (-f $srcfile) )
- {
- # if the source/dest file is binary, don't bother calculating
- # diffs, since they are likely undisplayable anyways
- if ( (-B $destfile && ! -z $destfile)
- || (-B $srcfile && ! -z $srcfile) )
- {
- $c->print(2, "Patching binary file $destfile");
- next;
- }
-
- my $diff = diff($destfile, $srcfile);
- my @diff = split(/\n/, $diff);
-
- if (length($diff))
- {
- $c->print(2, "updating $destfile");
-
- my $size = scalar(@diff);
-
- if (defined($max_diff_lines)
- and $max_diff_lines > 0
- and $size >= $max_diff_lines)
- {
- $c->print(2, "Changes to $destfile are too large to print("
- . "$size >= $max_diff_lines lines)");
- }
- else
- {
- foreach my $line (@diff)
- {
- $line =~ s/$tmpdir/spine-overlay/g;
- $c->cprint(" $line", 2, 0) if ($line =~ /^[+-]/);
- }
- }
- utime(time, time, $srcfile);
- }
- }
- elsif (not -e $destfile)
- {
- my $src_stat = lstat($srcfile);
-
- $c->print(2, "creating $destfile"
- . ' [mode ' . octal_conv($src_stat->mode) . ' |'
- . ' owner/group ' . uid_conv($src_stat->uid) . ':'
- . gid_conv($src_stat->gid) . ']');
- utime(time, time, $srcfile);
+sub build_disk_overlay {
+ my $c = shift;
+ my $settings = shift;
+
+ # we only deal with file URIs
+ return PLUGIN_SUCCESS unless substr( $settings->{uri}, 0, 5 ) eq "file:";
+
+ # XXX: we don't actauly support host yet, so it will be ignored
+ my ( $host, $overlay ) = ( $settings->{uri} =~ m%file://([^/]*)/(.*)% );
+
+ # does it start with a '/' if not then add in the croot as rsync gets upset
+ # FIXME: use filespec absolute...
+ unless ( $overlay =~ m%^/% ) {
+ $overlay = catfile( $c->getval("c_croot"), $overlay );
+ }
+
+ my $target = catfile( $settings->{tmpdir}, $settings->{path} );
+
+ # Deal with makeing sure '/' appears where needed and make sure the
+ # src exists
+ if ( -d $overlay ) {
+ $overlay .= "/" unless ( $overlay =~ m%/^% );
+ $target .= "/" unless ( $target =~ m%/^% );
+ } elsif ( -f $overlay ) {
+ if ( -d $target ) {
+ $target .= "/" unless ( $target =~ m%/^% );
}
- }
-
- unless ($DRYRUN)
- {
- if (do_rsync(%rsync_args) != SPINE_SUCCESS)
- {
- # These particular return values don't necessarily indicate that
- # the transfer wasn't at least partially successful. See the
- # rsync man page for more information. Necessary because /media
- # is read only almost everywhere but the mount point needs to be
- # created if the filesystem
- #
- # rtilder Wed Jun 6 14:08:49 PDT 2007
- my $rc = $? >> 8;
-
- if ($rc == 23 or $rc == 24)
- {
- $c->error("rsync partial failure! This is probably very bad.",
- 'err');
-
- if ($c->getval('partial_rsync_ok'))
- {
- return PLUGIN_SUCCESS;
- }
- }
-
- $c->error("rsync failed! Couldn't apply filesystem changes",
- 'err');
- return PLUGIN_FATAL;
- }
- }
-
- return PLUGIN_SUCCESS;
-}
-
-
-sub sync_attribs
-{
- $c = shift;
- my ($srcfile, $destfile) = @_;
-
- my $src_stat = lstat($srcfile);
- my $dest_stat = lstat($destfile);
-
- unless ($src_stat->mode eq $dest_stat->mode)
- {
- $c->print(2, "updating permissions on $destfile from " .
- octal_conv($dest_stat->mode) . " to " .
- octal_conv($src_stat->mode));
-
- chmod $src_stat->mode, $destfile
- unless ($DRYRUN);
- }
-
- unless ( ($src_stat->uid eq $dest_stat->uid) and
- ($src_stat->gid eq $dest_stat->gid) )
- {
- $c->print(2, "updating owner/group on $destfile from " .
- uid_conv($dest_stat->uid) . ":" .
- gid_conv($dest_stat->gid) . " to " .
- uid_conv($src_stat->uid) . ":" .
- gid_conv($src_stat->gid));
- }
-
- chown $src_stat->uid, $src_stat->gid, $destfile
- unless ($DRYRUN);
-}
-
-
-sub clean_overlay
-{
- $c = shift;
- my $tmpdir = $c->getval('c_tmpdir');
- my $tmplink = $c->getval('c_tmplink');
- my $rc = 0;
-
- # If --keep-overlay was specified on the command line, pretend like we did
- if ($DONTDELETE) {
- $c->print(1, "Leaving overlay in \"$tmpdir\"");
+ } else {
+ $c->print( 3, "disk overlay ($settings->{uri}) src does not exist" );
return PLUGIN_SUCCESS;
}
- $rc = remove_tmpdir($c, $tmpdir);
- unlink $tmplink;
-
- return PLUGIN_SUCCESS;
-}
-
-
-sub remove_tmpdir
-{
- my $c = shift;
- my $tmpdir = shift;
-
- # rm -rf paranoia.
- if ( ($tmpdir =~ m@^/tmp/.*@) and ($tmpdir =~ m@[^\.~]*@) )
+ $c->print( 3, "building disk overlay from $overlay to " . $target );
+ unless ( do_rsync( Config => $c,
+ Inert => 1,
+ Source => $overlay,
+ Target => $target,
+ Excludes => $settings->{excludes} ) )
{
- my $result = simple_exec(merge_error => 1,
- exec => 'rm',
- inert => 1,
- c => $c,
- args => "-rf $tmpdir");;
- return 1;
+ return PLUGIN_FATAL;
}
- return 0;
+ return PLUGIN_FINAL;
}
+sub descent_overlays {
+ my ( $c, $branch ) = @_;
-#
-# @ENTRIES is a module level global
-#
-sub find_changed
-{
- my $fname = $File::Find::name;
- # The target filename
- my (undef, $dest) = split(/$TMPDIR/, $fname);
+ # we only deal with file URIs
+ return PLUGIN_SUCCESS unless substr( $branch->{uri}, 0, 5 ) eq "file:";
- if ($fname eq $TMPDIR
- or $fname eq "$TMPDIR/")
- {
- return;
+ my ( undef, $descend_item ) = ( $branch->{uri} =~ m%file://([^/]*)/(.*)% );
+
+ # Note down any overlays related to this disk descend
+ my @overlay_map = ('overlay:/');
+ if ( exists $c->{'overlay_map'} ) {
+ @overlay_map = @{ $c->getvals("overlay_map") };
}
- my $lstat = lstat($fname);
- my $dstat = lstat($dest);
+ my $croot = $c->getval('c_croot');
- # Destination doesn't exist?
- unless (defined($dstat))
- {
- goto changed;
+ if ( $descend_item =~ m#^/# ) {
+ $descend_item = catfile( $croot, $descend_item );
}
- #
- # HAHA! I'm evil! unless ( ) { ... } elsif ( ) { ... } else { ... }
- #
- # rtilder Tue May 1 11:55:41 PDT 2007
- #
-
- # rdev seems unreliable on 2.4 kernels
- my $kernel_version = $c->getval('c_current_kernel_version');
- my $rdev = 0;
- if ($kernel_version =~ /^2\.4\./)
- {
- $rdev = 1;
- }
- elsif ($lstat->rdev == $dstat->rdev)
- {
- $rdev = 1;
- }
+ for my $element (@overlay_map) {
+ my ( $overlay, $target ) = split( /:/, $element );
+ $overlay = "${descend_item}/${overlay}/";
- # Any change in the important stat data? We disregard A, C, and M times
- # for the newly created overlay for obvious reasons
- unless ($lstat->mode == $dstat->mode
- and $lstat->uid == $dstat->uid
- and $lstat->gid == $dstat->gid
- and $lstat->size == $dstat->size
- and $rdev)
- {
- goto changed;
- }
- # If it's a file, are the contents the same?
- elsif (S_ISREG($lstat->mode) and S_ISREG($dstat->mode)) {
- my $currfh = new IO::File($dest);
- my $newfh = new IO::File($fname);
- my $curr = new Digest::MD5();
- my $new = new Digest::MD5();
-
- binmode $currfh;
- binmode $newfh;
-
- $curr->addfile($currfh);
- $new->addfile($newfh);
-
- $currfh->close();
- $newfh->close();
- undef $currfh;
- undef $newfh;
-
- unless ($curr->hexdigest() eq $new->hexdigest())
- {
- goto changed;
+ unless ( file_name_is_absolute($overlay) ) {
+ $overlay = catfile( $croot, $overlay );
+ $overlay .= '/'; # catfile() removes trailing slashes
}
- undef $curr;
- undef $new;
- }
- elsif (S_ISLNK($lstat->mode) and S_ISLNK($dstat->mode)) {
- my $curr_target = readlink $fname;
- my $new_target = readlink $dest;
-
- if ((defined($curr_target) and defined($new_target))
- and $curr_target ne $new_target) {
- goto changed;
- }
+ # Add the overlay to the overlays key
+ $c->getkey(SPINE_OVERLAY_KEY)->merge( { uri => "file:///$overlay",
+ bind => $target } );
}
- utime $dstat->atime, $dstat->mtime, $fname;
- return;
-
- changed:
- push @ENTRIES, $dest;
- return;
}
-
1;
Added: spine/branches/spine_2_2-devel/lib/Spine/Plugin/Overlay/HTTP.pm
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ spine/branches/spine_2_2-devel/lib/Spine/Plugin/Overlay/HTTP.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -0,0 +1,150 @@
+# -*- mode: perl; cperl-continued-brace-offset: -4; indent-tabs-mode: nil; -*-
+# vim:shiftwidth=2:tabstop=8:expandtab:textwidth=78:softtabstop=4:ai:
+
+# $Id$
+
+#
+# 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.
+#
+# 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/>.
+#
+# (C) Copyright Ticketmaster, Inc. 2007
+#
+
+use strict;
+
+package Spine::Plugin::Overlay::HTTP;
+use base qw(Spine::Plugin);
+use File::Spec::Functions;
+use Spine::Constants qw(:plugin);
+use Spine::Util qw(create_exec mkdir_p);
+
+our ( $VERSION, $DESCRIPTION, $MODULE, $DONTDELETE, $TMPDIR, @ENTRIES );
+
+$VERSION = sprintf( "%d", q$Revision$ =~ /(\d+)/ );
+$DESCRIPTION = "HTTP based overlays.";
+
+$MODULE = { author => 'oss...@ticketmaster.com',
+ description => $DESCRIPTION,
+ version => $VERSION,
+ hooks => {
+ 'PREPARE/Overlay/build' => [
+ { name => 'build_http_overlay',
+ code => \&build_http_overlay }
+ ], } };
+
+use Spine::Constants qw(:basic);
+
+#TODO: might be good to re-implement using LWP
+#TODO: implement excludes (probabley after LWP)
+sub build_http_overlay {
+ my $c = shift;
+ my $settings = shift;
+
+ # we only deal with http URIs
+ return PLUGIN_SUCCESS unless $settings->{uri} =~ m/^https?:/;
+
+ my ( $proto, $host, $location ) =
+ ( $settings->{uri} =~ m%([^:]*)://([^/]*)/(.*)% );
+
+ # because wget needs to know how much to strip of the url we need
+ # to be able to count the parts
+ # FIXME: should use a file spec function....
+ my @loc_parts = split( "/", $location );
+
+ $c->print( 3, "building http based overlay from " . $settings->{uri} );
+
+ # create the path within our tempory location
+ mkdir_p( catfile( $settings->{tmpdir}, $settings->{path} ) );
+
+ # create the wget exec object
+ my $wget = create_exec(
+ exec => "wget",
+ args => [ "-N",
+ "--no-parent",
+ "-r",
+ "-nH",
+ "--progress=dot",
+ "--cut-dirs",
+ scalar(@loc_parts),
+ "-P",
+ catfile( $settings->{tmpdir}, $settings->{path} ),
+ $settings->{uri} ],
+ c => $c,
+ merge_error => 1,
+ inert => 1 );
+
+ # this will normally be related to wget being missing, because we didn't
+ # select "quiet" when we created the object the error should have been
+ # printed.
+ unless ( $wget->ready() ) {
+ return PLUGIN_ERROR;
+ }
+
+ # GO
+ $wget->start();
+
+ my ( $src, $dst );
+ my @remove;
+ my $uri = $settings->{uri};
+
+ while ( my $line = $wget->readline() ) {
+ chomp($line);
+ $c->cprint( $line, 5 );
+
+ if ( $line =~ m%^-.*$uri(.*)$% ) {
+ $src = $1;
+ # src can be null if it's the first request
+ unless ( $src ) {
+ if ( $uri =~ m%/$% ) {
+ # The uri was a dir
+ $src = "/";
+ } else {
+
+ # the uri was direct to a file
+ # XXX: this means it's important to have a traling '/' in
+ # URIs
+ $src = $uri;
+ $src =~ s%.*/%%;
+ }
+ } else {
+ $src = $1;
+ }
+ } elsif ( $line =~ m/^saving to:\s+.([^`']+).\s*$/i ) {
+ $dst = $1;
+
+ # If the file is index.html and the src was a directory
+ # then we need to clean out the index file. If however
+ # there really was an index.html file we want to keep it
+ if ( $dst =~ m/index\.html$/ && $src =~ m%/$% ) {
+ push @remove, $dst;
+ $c->cprint( "Getting directory ($src)", 3 );
+ } else {
+ $c->cprint( "Getting ($src) as ($dst)", 3 );
+ }
+ }
+ }
+
+ # Just in case...
+ $wget->isrunning() || $wget->wait();
+
+ foreach (@remove) {
+ $c->cprint("Removing index file ($_)", 3);
+ unlink ($_);
+ }
+
+ # Did it exit with an error?
+ return PLUGIN_ERROR unless ( $wget->exitstatus() == 0 );
+
+ return PLUGIN_SUCCESS;
+}
+
+1;
Modified: spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/Basic.pm
==============================================================================
--- spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/Basic.pm Tue Mar 30 08:35:44 2010 (r317)
+++ spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/Basic.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -26,11 +26,10 @@
use File::Spec::Functions;
use Spine::Registry;
-
our ( $VERSION, $DESCRIPTION, $MODULE );
my $CPATH;
-$VERSION = sprintf( "%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/ );
+$VERSION = sprintf( "%d", q$Revision$ =~ /(\d+)/ );
$DESCRIPTION = "Parselet::Basic, processes Basic keys";
$MODULE = {
@@ -39,34 +38,36 @@
version => $VERSION,
hooks => {
'PARSE/key' => [
- { name => "Basic_Init",
- code => \&_init_key,
+ { name => "Basic_Init",
+ code => \&_init_key,
provides => [ 'retrieve', 'basic_init' ], },
- { name => "Basic_Parse_Lines",
- code => \&_preprocess,
+ { name => "Basic_Parse_Lines",
+ code => \&_preprocess,
provides => [ 'preprocess', 'PARSE/key/line' ], },
{ name => "Basic_Final",
code => \&_parse_basic_key,
+
# this does finalization
provides => [ 'simple', 'basic_key' ], }, ], }, };
# preprocess keys that are scalars, scalar refs or filenames
sub _init_key {
- my ( $c, $data ) = @_;
+ my ( $c, $obj ) = @_;
# Do we need to read a file?
- if ( exists $data->{source} && $data->{source} =~ m/^file:(.*)$/
- && !defined $data->{obj} )
+ my $source = $obj->metadata("uri");
+ if ( $source
+ && $source =~ m%^file://[^/]*/(.*)$%
+ && !$obj->does_exist() )
{
- $data->{file} = $1;
+ my $file = $1;
my $fh = undef;
- my $file = $data->{file};
+
unless ( -f $file ) {
$file = catfile( $c->{c_croot}, $file );
- if ( -f $file ) {
- $data->{file} = $file;
- }
}
+
+ $obj->metadata_set( 'file', $file );
unless ( -r $file ) {
$c->error( "Can't read file \"$file\"", 'crit' );
return PLUGIN_ERROR;
@@ -79,21 +80,23 @@
$c->print( 4, "reading key $file" );
# Trun the file into an array
- $data->{obj} = join('', $fh->getlines());
+ $obj->set( join( '', $fh->getlines() ) );
close($fh);
+ }
- # Is this a ref to a scalar
- } elsif ( ref( $data->{obj} ) eq "SCALAR" ) {
- $data->{obj} = ${$data->{obj}};
+ # make sure there is always a description
+ unless ( defined $obj->metadata("description") ) {
+ $obj->metadata_set( "description", $source );
}
-
+
return PLUGIN_SUCCESS;
}
+# If we have a sclar then we allow plugins to process each line
sub _preprocess {
- my ( $c, $data ) = @_;
+ my ( $c, $obj ) = @_;
- if (ref($data->{obj})) {
+ if ( ref( $obj->get() ) ) {
return PLUGIN_SUCCESS;
}
@@ -101,13 +104,15 @@
my $lineno = 0;
my $point = $registry->get_hook_point("PARSE/key/line");
- my $buf = "";
- while ($data->{obj} =~ /([^\r\n]*[\r\n]*)/sog) {
+ my $buf = "";
+ my $data = $obj->get();
+ while ( $data =~ /([^\r\n]*[\r\n]*)/sg ) {
my $line = $1;
$lineno++;
-
- my $rc = $point->run_hooks_until( PLUGIN_ERROR, $c, \$line,
- $lineno, $data );
+
+ my $rc =
+ $point->run_hooks_until( PLUGIN_ERROR, $c, \$line, $lineno, $obj );
+
# This should never happen
if ( $rc & PLUGIN_ERROR ) {
$c->error( "Error parsing key line", 'crit' );
@@ -115,26 +120,22 @@
}
$buf .= $line if defined $line;
}
- $data->{obj} = $buf;
+ $obj->set($buf);
return PLUGIN_SUCCESS;
}
-
# This gets called near the end, it will skip
# anything that has been turned into a ref
sub _parse_basic_key {
- my ( $c, $data ) = @_;
+ my ( $c, $obj ) = @_;
# Skip refs, only scalars
- if ( ref( $data->{obj} ) ) {
+ if ( $obj->does_exist() && ref( $obj->get() ) ) {
return PLUGIN_SUCCESS;
}
-#use Data::Dumper; print Dumper($data->{obj});
-
# Ignore comments and blank lines.
- $data->{obj} =
- [ grep( !/^\s*(?:#.*)?$/, split( m/[\n\r]+/, $data->{obj} ) ) ];
+ $obj->set( [ grep( !/^\s*(?:#.*)?$/, split( m/[\n\r]+/, $obj->get() ) ) ] );
return PLUGIN_SUCCESS;
}
Modified: spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/Complex.pm
==============================================================================
--- spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/Complex.pm Tue Mar 30 08:35:44 2010 (r317)
+++ spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/Complex.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -30,7 +30,7 @@
my $CPATH;
-$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+$VERSION = sprintf("%d", q$Revision$ =~ /(\d+)/);
$DESCRIPTION = "Parselet::Complex, detects if the buffer is a complex key";
$MODULE = { author => 'oss...@ticketmaster.com',
@@ -45,17 +45,17 @@
# This means we only have to check if the key is complex once
# and cut's down the number of plugins we have to cascade through
sub check_complex {
- my ($c, $data) = @_;
+ my ($c, $obj) = @_;
# only scalars
- if (ref($data->{obj})) {
+ if (ref($obj->get())) {
return PLUGIN_SUCCESS;
}
# If the object contains something that looks
# like an indication of a complex type we parse it
# it's not a problem if we make a mistake however.
- unless ($data->{obj} =~ m/^#?%/) {
+ unless ($obj->get() =~ m/^#?%/) {
return PLUGIN_SUCCESS;
}
@@ -64,7 +64,7 @@
# HOOKME: Complex keys
my $point = $registry->get_hook_point("PARSE/key/complex");
# HOOKME, go through ALL complex plugins
- my $rc = $point->run_hooks_until(PLUGIN_FATAL, $c, $data);
+ my $rc = $point->run_hooks_until(PLUGIN_FATAL, $c, $obj);
if ($rc & PLUGIN_FATAL) {
$c->error("There was a problem processing key data", 'crit');
return PLUGIN_ERROR;
Modified: spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/DNS.pm
==============================================================================
--- spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/DNS.pm Tue Mar 30 08:35:44 2010 (r317)
+++ spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/DNS.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -30,7 +30,7 @@
our ($VERSION, $DESCRIPTION, $MODULE);
my $resolver;
-$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+$VERSION = sprintf("%d", q$Revision$ =~ /(\d+)/);
$DESCRIPTION = "Parselet::DNS, Will create an object from DNS";
$MODULE = { author => 'oss...@ticketmaster.com',
@@ -45,38 +45,38 @@
# Currently this is a simple example plugin. It should be expanded to allow
# more complicated things to be done.
sub dns_key {
- my ($c, $data) = @_;
+ my ($c, $obj) = @_;
$resolver = Net::DNS::Resolver->new unless $resolver;
# Is it for us?
- unless ($data->{obj}->{dynamic_type} =~ m/^\s*dns\s+lookup\s*$/i) {
+ unless ($obj->get->()->{dynamic_type} =~ m/^\s*dns\s+lookup\s*$/i) {
return PLUGIN_SUCCESS;
}
- my $obj = $data->{obj};
+ my $data = $obj->get();
- unless (exists $obj->{query}) {
+ unless (exists $data->{query}) {
$c->error("Missing 'query' from dns lookup", 'crit');
return PLUGIN_ERROR;
}
- $obj = $obj->{query};
+ $data = $data->{query};
- unless (exists $obj->{type}) {
+ unless (exists $data->{type}) {
$c->error("Missing query 'type' from dns lookup", 'crit');
return PLUGIN_ERROR;
}
- if ($obj->{type} =~ m/axfr/i) {
- if (exists $obj->{domain}) {
- $data->{obj} = do_axfr($obj->{domain});
+ if ($data->{type} =~ m/axfr/i) {
+ if (exists $data->{domain}) {
+ $obj->set(do_axfr($obj->{domain}));
return PLUGIN_FINAL;
}
$c->error("Missing query 'domain' from AXFR lookup", 'crit');
return PLUGIN_ERROR;
- } elsif ($obj->{type} =~ m/host/i) {
- if (exists $obj->{host}) {
- $data->{obj} = lookup_host($obj->{host});
+ } elsif ($data->{type} =~ m/host/i) {
+ if (exists $data->{host}) {
+ $obj->seT(lookup_host($obj->{host}));
return PLUGIN_FINAL;
}
$c->error("Missing query 'host' from HOST lookup", 'crit');
Modified: spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/Dynamic.pm
==============================================================================
--- spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/Dynamic.pm Tue Mar 30 08:35:44 2010 (r317)
+++ spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/Dynamic.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -31,7 +31,7 @@
my $init = 0;
-$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+$VERSION = sprintf("%d", q$Revision$ =~ /(\d+)/);
$DESCRIPTION = "Parselet::Dynamic, detects if the object can be expanded";
$MODULE = { author => 'oss...@ticketmaster.com',
@@ -46,16 +46,21 @@
# This means we only have to check if the key is dynamic once
# and cut's down the number of plugins we have to cascade through
sub check_dynamic {
- my ($c, $data) = @_;
+ my ($c, $obj) = @_;
+ my $data = $obj->get();
+
# only hash refs / complex
- unless (ref($data->{obj}) eq 'HASH') {
+ unless (ref($data) eq 'HASH') {
return PLUGIN_SUCCESS;
}
+
+ # we support both dynamix_type and advanced_type
# If the object contains dynamic_type then we
# will kick it through the PARSE/key/dynamic phase
- unless (exists $data->{obj}->{dynamic_type}) {
+ unless (exists $data->{dynamic_type} ||
+ exists $data->{advanced_type}) {
return PLUGIN_SUCCESS;
}
@@ -64,7 +69,7 @@
# HOOKME: Dynamic complex keys
my $point = $registry->get_hook_point("PARSE/key/dynamic");
# HOOKME, go through ALL dynamic plugins
- my $rc = $point->run_hooks_until(PLUGIN_FATAL, $c, $data);
+ my $rc = $point->run_hooks_until(PLUGIN_FATAL, $c, $obj);
if ($rc & PLUGIN_FATAL) {
$c->error("There was a problem getting key data", 'crit');
return PLUGIN_ERROR;
Modified: spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/JSON.pm
==============================================================================
--- spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/JSON.pm Tue Mar 30 08:35:44 2010 (r317)
+++ spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/JSON.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -31,7 +31,7 @@
our ($VERSION, $DESCRIPTION, $MODULE);
my $CPATH;
-$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+$VERSION = sprintf("%d", q$Revision$ =~ /(\d+)/);
$DESCRIPTION = "Parselet::JSON, processes JSON keys";
$MODULE = { author => 'oss...@ticketmaster.com',
@@ -45,16 +45,19 @@
};
sub _parse_json_key {
- my ($c, $data) = @_;
+ my ($c, $obj) = @_;
+ my $data = $obj->get();
+
# Skip refs, only scalars
- if (ref($data->{obj})) {
+ if (ref($data)) {
return PLUGIN_SUCCESS;
}
- if ( $data->{obj} =~ m/^#?%JSON/ ) {
- $data->{obj} = JSON::Syck::Load($data->{obj});
- if (defined ($data->{obj})) {
+ if ( $data =~ m/^#?%JSON/ ) {
+ $data = JSON::Syck::Load($data);
+ if (defined ($data)) {
+ $obj->set($data);
return PLUGIN_SUCCESS;
}
return PLUGIN_ERROR;
Modified: spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/Operator.pm
==============================================================================
--- spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/Operator.pm Tue Mar 30 08:35:44 2010 (r317)
+++ spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/Operator.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -1,6 +1,6 @@
# vim:shiftwidth=2:tabstop=8:expandtab:textwidth=78:softtabstop=4:ai:
-# $Id: Operator.pm 251 2009-09-03 16:17:59Z richard $
+# $Id$
#
# This program is free software; you can redistribute it and/or modify
@@ -23,58 +23,39 @@
package Spine::Plugin::Parselet::Operator;
use base qw(Spine::Plugin);
use Spine::Constants qw(:plugin);
-### TODO Need to write out own....
-#use List::MoreUtils qw(uniq);
+use Scalar::Util 'blessed';
+use Spine::Key;
our ( $VERSION, $DESCRIPTION, $MODULE );
my $CPATH;
-$VERSION = sprintf( "%d.%02d", q$Revision: 251 $ =~ /(\d+)\.(\d+)/ );
+$VERSION = sprintf( "%d", q$Revision$ =~ /(\d+)/ );
$DESCRIPTION = "Parselet::Operator, processes any operators";
-$MODULE = {
- author => 'oss...@ticketmaster.com',
- description => $DESCRIPTION,
- version => $VERSION,
- hooks => {
- 'PARSE/key' => [
- { name => "ApplyOperators",
- code => \&process_operators,
- provides => [ 'merge', 'applied_operators' ] } ],
- 'PARSE/key/line' => [
- { name => "DetectOperators",
- code => \&parse_line,
- provides => ['detected_operatos'] } ]
- }
-};
+$MODULE = { author => 'oss...@ticketmaster.com',
+ description => $DESCRIPTION,
+ version => $VERSION,
+ hooks => {
+ 'PARSE/key' => [
+ { name => "ApplyOperators",
+ code => \&process_operators,
+ provides => [ 'merge', 'applied_operators' ]
+ } ],
+ 'PARSE/key/line' => [{ name => "DetectOperators",
+ code => \&parse_line,
+ provides => ['detected_operatos']
+ } ] } };
use constant CONTROL_PREFIX => "spine_";
-use constant { HASH_KEY => 1 << 0,
- ARRAY_KEY => 1 << 1,
- SCALAR_KEY => 1 << 2,
- NONREF_KEY => 1 << 3,
- BLANK_KEY => 1 << 4, };
-use constant MULTI_KEY => ( HASH_KEY | ARRAY_KEY );
-
-# Resolve ref returns into numeric versions
-my %key_type = ( HASH => HASH_KEY,
- ARRAY => ARRAY_KEY,
- SCALAR => SCALAR_KEY,
- +undef => NONREF_KEY, );
-
-# <OPNAME> => [ <OP_FUNCREF>, <KEYTYPS> ]
-# undef = a currently blank key
-my %op_func = ( replace => [ \&replace_key, MULTI_KEY ],
- merge => [ \&merge_key, MULTI_KEY ],
- grep => [ \&grep_key, MULTI_KEY ],
- remove => [ \&rgrep_key, MULTI_KEY ], );
-
sub parse_line {
- my ( $c, $line_ref, $line_no, $data ) = @_;
+ my ( $c, $line_ref, $line_no, $obj ) = @_;
+
+ my $operators = $obj->metadata("operators");
+ if ( !defined($operators) ) {
+ $obj->metadata_set( "operators", ( $operators = [] ) );
+ }
- # Create array ref for control operators
- $data->{control} = [] unless exists $data->{control};
my $re;
# This allows users to have something that
@@ -89,14 +70,14 @@
# Detect legacy '=' operator and convert
if ( $line_no == 1 && $$line_ref =~ m/^=\s*$/ ) {
- unshift @{ $data->{control} }, ["replace"];
+ push @$operators, ["replace"];
$$line_ref = undef;
return PLUGIN_SUCCESS;
}
# Detect legacy '-' operator and convert
if ( $$line_ref =~ m/^-(.*)/ ) {
- push @{ $data->{control} }, [ "remove", $1 ];
+ push @$operators, [ "remove", $1 ];
$$line_ref = undef;
return PLUGIN_SUCCESS;
}
@@ -105,162 +86,117 @@
# these will be processed by another plugin
$re = CONTROL_PREFIX . '(\w+)\((.*)\)\s*';
if ( $$line_ref =~ m/^$re$/ ) {
- push @{ $data->{control} }, [ $1, $2 ];
- next;
+ my ( $op, $opts ) = ( $1, $2 );
+ push @$operators, [ $1, $2 ];
+ $$line_ref = undef;
+ return PLUGIN_SUCCESS;
}
+
+ return PLUGIN_SUCCESS;
}
-# XXX: might be worth having a PARSE/key/control hook point
-# to make this more plugable... we go for speed for now...
+# we take any operators set along the way and apply them to the cur obj
+# if there is one.
+# Then we set reslult to be the final object
sub process_operators {
- my ( $c, $data ) = @_;
+ my ( $c, $new_obj, $cur_obj, $result_ref ) = @_;
+ # if the result ref is defined then we should do nothing
+ return PLUGIN_SUCCESS if ( defined $$result_ref );
+
+ # get + remove operators from the new object
+ my $operators = $new_obj->metadata_remove("operators");
+
+ # if there is no cur_obj then there isn't much to do
+ if (!defined $cur_obj) {
+ $$result_ref = $new_obj;
+ return PLUGIN_SUCCESS;
+ } else {
+ $$result_ref = $cur_obj;
+ }
+
+ # add in any default operators that are kept between runs
+ # these are put at the start so that the user can override
+ my $default_ops = $new_obj->metadata("default_operators");
+ if ( ref $default_ops eq "ARRAY" ) {
+ unshift @$operators, @$default_ops;
+ }
+
+ my $new_data_ref = $new_obj->get_ref();
+
+ # default merge/replace polocy, We unshift so that it takes lowest
+ # priority making it easy for the user to override
+ if ( $cur_obj->can("merge_default")
+ && $cur_obj->merge_default($new_data_ref) )
+ {
+ unshift @$operators, ["merge"];
+ } else {
+ unshift @$operators, ["replace"];
+ }
+ # TODO: depreciate
# Detect legacy '=' operator and convert. Normally this would happen during
# PARSE/key/line but because comments can still exists in the key at that
# point we have to check here as well. Eventually this can be removed
# once people use the new format.
- if ( ref($data->{obj}) eq "ARRAY"
- && exists $data->{obj}->[0]
- && $data->{obj}->[0] =~ m/^=\s*$/ )
+ if ( ref($$new_data_ref) eq "ARRAY"
+ && exists ${$new_data_ref}->[0]
+ && ${$new_data_ref}->[0] =~ m/^=\s*$/ )
{
- shift @{$data->{obj}};
- $data->{control} = [] unless exists $data->{control};
- unshift @{ $data->{control} }, ["replace"];
+ shift @{$$new_data_ref};
+ push @$operators, ["replace"];
}
# anythign for us to do?
- unless ( exists $data->{control} && length( $data->{control} ) ) {
+ unless ( scalar(@$operators) ) {
return PLUGIN_SUCCESS;
}
- # If there is no keyname then there will be nothing
- # for us to operate on
- return PLUGIN_SUCCESS
- unless ( exists $data->{keyname}
- && defined $data->{keyname} );
- my $keyname = $data->{keyname};
-
- # What type are we putting in place
- my $objtype = $key_type{ ref( $data->{obj} ) };
-
- # get the current key content, ops are pointless without it
- my $current_key = $c->getkey($keyname);
- return PLUGIN_SUCCESS unless defined $current_key;
- my $keytype = $key_type{ ref($current_key) };
-
- # If both are arrays then we add in a merge by default here
- # If the user doesn't want this they have to use replace
- # to clear out the src so that this will do nothing
- if ( $keytype == ARRAY_KEY && $objtype == ARRAY_KEY ) {
- push @{ $data->{control} }, ["merge"];
- }
-
+ # the operators we will actually run
+ my @final_ops;
- foreach my $op ( @{ $data->{control} } ) {
-
- # Does the operator exists?
- unless ( exists $op_func{ $op->[0] } ) {
- $c->error( "Attempt to use a key operator that we don't know."
- . " key = ($keyname) operator = ($op->[0]).",
- "error" );
- next;
- }
-
- # Does the operator support this type of key?
- unless ( $keytype & $op_func{ $op->[0] }->[1] ) {
-
- # TODO shorten this error message
- $c->error(
- "Attempt to use key operator \"$op->[0]\" on \"$keyname\" "
- . "which is an unsupported key type \""
- . ref($current_key) . "\".",
- "error" );
+ # Merge and replace are special cases as they have to come last
+ # and only one of them makes sense. We want the last one given to
+ # take effect so the user can always overried any defualts
+ my $merge = undef;
+ while ( my $op = shift @$operators ) {
+ if ( $op->[0] eq "replace" ) {
+ $merge = undef;
+ } elsif ( $op->[0] eq "merge" ) {
+ shift @$op;
+
+ # we want merge to be defined even if there is
+ # no options so we create an empty array ref
+ $merge = exists $op->[0] ? $op : [];
+ } else {
+ push @final_ops, $op;
}
-
- # Call the operator func, it's up to it to alter the object if needed
- # the return goes into.
- $current_key =
- &{ $op_func{ $op->[0] }->[0] }( $c, $keytype, $current_key, $objtype,
- $data, $op->[1] );
- }
-
- return PLUGIN_SUCCESS;
-}
-
-# Simple function that will clear out the current key
-sub replace_key {
- my ( $c, $ktype, $ckey, $otype, $data, $opts ) = @_;
-
-
- $c->print(4, "replaceing the \"$data->{keyname}\" key");
- return {} if $ktype == HASH_KEY;
- return [] if $ktype == ARRAY_KEY;
- return undef;
-}
-
-sub merge_key {
- my ( $c, $ktype, $ckey, $otype, $data, $opts ) = @_;
-
-
- # We can only merge the same types
- unless ( $ktype eq $otype) {
- $c->error( "Attempt to merge \"$data->{source}\" into \"$data->{keyname}\" of wrong type",
- "error" );
- return $ckey;
}
- # If the key has no length then no point merging.
- #return $ckey unless scalar(@$ckey);
+ my $description = $cur_obj->metadata("description") || ref($cur_obj);
- $c->print( 4, "mergeing \"$data->{source}\" into the \"$data->{keyname}\" key" );
+ foreach my $op (@final_ops) {
+ my $method = $op->[0];
- # Split out any options
- $opts = { map { chomp($_); $_ => undef } split( ",", $opts ) };
-
- if ( $ktype == HASH_KEY ) {
- if ( exists $opts->{reverse} ) {
- $data->{obj} = { %{ $data->{obj} }, %{$ckey} };
- } else {
- $data->{obj} = { %{$ckey}, %{ $data->{obj} } };
+ # Does the operator exists?
+ unless ( $cur_obj->can($method) ) {
+ $c->error( "Attempt to use unknown operator ($method) on "
+ . $description,
+ "error" );
+ return PLUGIN_ERROR;
}
- return {};
- }
- if ( $ktype == ARRAY_KEY ) {
- if ( exists $opts->{reverse} ) {
- push @{ $data->{obj} }, @{$ckey};
- } else {
- unshift @{ $data->{obj} }, @{$ckey};
- }
- #### TODO: write unique func
- #@{$data->{obj}} = uniqu(@{$data->{obj}}) if exists $opts->{uniqu};
+ # hopefully the call to "can" above make this fairly safe...
+ $cur_obj->$method( exists $op->[1] ? $op->[1] : undef );
}
-}
-
-sub grep_key {
- my ( $c, $ktype, $ckey, $otype, $data, $opts ) = @_;
- $c->print( 4, "greppping \"$opts\" from the \"$data->{keyname}\" key" );
-
- return [ grep /$opts/, @{$ckey} ] if $ktype == ARRAY_KEY;
-
- foreach ( keys %$ckey ) {
- delete $ckey->{$_} unless $_ =~ m/$opts/;
+ # Finally deal with replace/merge
+ if ( defined $merge ) {
+ $cur_obj->merge($new_obj, @$merge);
+ } else {
+ $cur_obj->replace($new_obj, @$merge);
}
- return $ckey;
-}
-sub rgrep_key {
- my ( $c, $ktype, $ckey, $otype, $data, $opts ) = @_;
-
- $c->print( 4, "removing \"$opts\" from the \"$data->{keyname}\" key" );
-
- return [ grep !/$opts/, @{$ckey} ] if $ktype == ARRAY_KEY;
-
- foreach ( keys %$ckey ) {
- delete $ckey->{$_} if $_ =~ m/$opts/;
- }
- return $ckey;
+ return PLUGIN_SUCCESS;
}
1;
Modified: spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/YAML.pm
==============================================================================
--- spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/YAML.pm Tue Mar 30 08:35:44 2010 (r317)
+++ spine/branches/spine_2_2-devel/lib/Spine/Plugin/Parselet/YAML.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -29,7 +29,7 @@
our ($VERSION, $DESCRIPTION, $MODULE);
my $CPATH;
-$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+$VERSION = sprintf("%d", q$Revision$ =~ /(\d+)/);
$DESCRIPTION = "Parselet::YAML, processes YAML keys";
$MODULE = { author => 'oss...@ticketmaster.com',
@@ -42,18 +42,21 @@
};
sub _parse_yaml_key {
- my ($c, $data) = @_;
+ my ($c, $obj) = @_;
+
+ my $data = $obj->get();
# Skip refs, only scalars
- if (ref($data->{obj})) {
+ if (ref($data)) {
return PLUGIN_SUCCESS;
}
- if ( $data->{obj} =~ m/^#?%YAML\s+(\d+\.\d+)/ ) {
+ if ( $data =~ m/^#?%YAML\s+(\d+\.\d+)/ ) {
return PLUGIN_ERROR if ($1 ne "1.0");
- $data->{obj} = YAML::Syck::Load($data->{obj});
- if (defined ($data->{obj})) {
+ $data = YAML::Syck::Load($data);
+ if (defined ($data)) {
+ $obj->set($data);
return PLUGIN_SUCCESS;
}
return PLUGIN_ERROR;
Modified: spine/branches/spine_2_2-devel/lib/Spine/Plugin/SystemInfo.pm
==============================================================================
--- spine/branches/spine_2_2-devel/lib/Spine/Plugin/SystemInfo.pm Tue Mar 30 08:35:44 2010 (r317)
+++ spine/branches/spine_2_2-devel/lib/Spine/Plugin/SystemInfo.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -148,7 +148,8 @@
$c->{c_local_netmask} = $netmask;
$c->{c_netcard} = $netcard;
- $c->get_values("platform/$platform");
+ # FIXME: this is reall reall bad, there should be a platform uri key
+ $c->read_config_branch(uri => "file:///platform/$platform");
return PLUGIN_SUCCESS;
}
Modified: spine/branches/spine_2_2-devel/lib/Spine/Plugin/Templates.pm
==============================================================================
--- spine/branches/spine_2_2-devel/lib/Spine/Plugin/Templates.pm Tue Mar 30 08:35:44 2010 (r317)
+++ spine/branches/spine_2_2-devel/lib/Spine/Plugin/Templates.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -73,10 +73,11 @@
# This is based on the origianl _convert_lame_to_TT but works on a multi
# line scalar not an array element.
sub _convert_to_TT {
- my $data = shift;
+ my $obj = shift;
+ my $data = $obj->get_ref();
my $legacy = 0;
- while ( $data->{obj} =~ m/(\[%(\s*)(IF|MATCH|ELSIF)\s+(.+?)%\])/sg ) {
- my $new_pos = pos($data->{obj}); # This is the pos of the end of match
+ while ( $$data =~ m/(\[%(\s*)(IF|MATCH|ELSIF)\s+(.+?)%\])/sg ) {
+ my $new_pos = pos($$data); # This is the pos of the end of match
my $data_length = length($1);
my $new = "[\%$2" . (( $3 eq 'MATCH' or $3 eq 'IF' ) ? 'IF' : 'ELSIF');
@@ -96,9 +97,9 @@
$new .= ' ' . join( ' AND ', @conditions ) . " \%]";
# Replace within the string
- substr($data->{obj}, ($new_pos - $data_length), $data_length, $new);
+ substr($$data, ($new_pos - $data_length), $data_length, $new);
# Set the position for the next iteration to after out insert (magic)
- pos($data->{obj}) = ($new_pos - $data_length) + length($new);
+ pos($$data) = ($new_pos - $data_length) + length($new);
# Take note that this is a legacy key
$legacy = 1;
}
@@ -355,9 +356,10 @@
sub _templatize_key
{
- my ($c, $data) = @_;
+ my ($c, $obj) = @_;
my $ttdata = { c => $c };
+ my $data = $obj->get_ref();
# $output = \$output would be a cicular ref (as expected)
# so we have to use another var.
@@ -371,13 +373,13 @@
#}
# Skip refs, only scalars
- if (ref($data->{obj})) {
+ if (ref($$data)) {
return PLUGIN_SUCCESS;
}
# XXX: remove in version 2.3
- if (_convert_to_TT($data)) {
- $c->error("$data->{source} uses depreciated flow control syntax",
+ if (_convert_to_TT($obj)) {
+ $c->error($obj->metadata("description") . " uses depreciated flow control syntax",
"warning");
}
@@ -387,17 +389,16 @@
$KEYTT = new Template( { CACHE_SIZE => 0 } );
}
- my $template = $data->{obj};
# Note the $template is passed in as a reference to make sure it isn't
# mistaken for a filename
- unless (defined($KEYTT->process(\$data->{obj}, $ttdata, $output))) {
+ unless (defined($KEYTT->process($data, $ttdata, $output))) {
# FIXME, implement error reporting
$c->error("could not process templatized key ($data->{source}): "
. $KEYTT->error(), 'err');
return PLUGIN_ERROR;
}
- $data->{obj} = ${$output};
+ $obj->set(${$output});
return PLUGIN_SUCCESS;
}
Modified: spine/branches/spine_2_2-devel/spine-mgmt.conf
==============================================================================
--- spine/branches/spine_2_2-devel/spine-mgmt.conf Tue Mar 30 08:35:44 2010 (r317)
+++ spine/branches/spine_2_2-devel/spine-mgmt.conf Tue Mar 30 08:39:16 2010 (r318)
@@ -1,7 +1,7 @@
[spine]
ConfigSource = ISO9660
StateDir = /var/spine-mgmt
-Profile = StandardPlugins
+Profile = FullWithAPT
SyslogIdent = spine
SyslogFacility = local3
SyslogOptions = ndelay,pid
@@ -12,32 +12,32 @@
Timeout = 5
[DefaultPlugins]
-DISCOVERY/populate = DryRun SystemInfo
+DISCOVERY/populate = Overlay DescendOrder Descend::Disk DryRun SystemInfo
DISCOVERY/policy-selection = DescendOrder
-PARSE/complete = Interpolate
+DISCOVERY/Descend/resolve = Descend::Disk
+PARSE/branch = Data::Disk
PARSE/key = Parselet::Basic Parselet::Complex Parselet::Operator Parselet::Dynamic Templates
PARSE/key/line = Parselet::Operator
PARSE/key/complex = Parselet::JSON Parselet::YAML
PARSE/key/dynamic = Parselet::DNS
+PARSE/complete = Interpolate
+PREPARE/Overlay/load = Overlay::Disk
+PREPARE/Overlay/build = Overlay::Disk Overlay::HTTP
+
# Basic safe run
[StandardPlugins]
-#
-# Templates here is for quicktemplate functionality
-#
-PREPARE = PrintData Templates Overlay
-EMIT = Templates
+PREPARE = Overlay PrintData Templates
APPLY = Overlay RestartServices Finalize
CLEAN = Overlay
+EMIT = Templates
#
# Full profile with APT and all the features enabled
#
[FullWithAPT]
-DISCOVERY/populate = DryRun SystemInfo
-DISCOVERY/policy-selection = DescendOrder
PARSE/complete = Auth Interpolate
-PREPARE = PrintData Templates Overlay
+PREPARE = Overlay PrintData Templates
EMIT = Templates Auth
APPLY = Overlay RPMPackageManager Overlay SystemHarden TweakStartup RestartServices Finalize
CLEAN = Overlay RPMPackageManager
@@ -47,49 +47,11 @@
# profile is that FirstBoot doesn't restart any services or delete any packages
#
[FirstBoot]
-PREPARE = PrintData Templates Overlay
+PREPARE = Overlay PrintData Templates Overlay
EMIT = FirstRun Templates
APPLY = Overlay Finalize
CLEAN = Overlay
-#
-# First boot profile that corresponds to the Full profile above
-#
-[FirstBootFullWithAPT]
-DISCOVERY/populate = DryRun SystemInfo
-DISCOVERY/policy-selection = DescendOrder
-PARSE/complete = Auth Interpolate
-PREPARE = PrintData Templates Overlay
-EMIT = Templates Auth
-APPLY = Overlay RPMPackageManager Overlay SystemHarden TweakStartup Finalize
-CLEAN = Overlay
-
-#
-# Full profile with new PackageManager and all the features enabled
-#
-[FullWithPKGMGR]
-DISCOVERY/populate = DryRun SystemInfo
-DISCOVERY/policy-selection = DescendOrder
-PARSE/complete = Auth Interpolate PackageManager
-PARSE/initialize = PackageManager
-PREPARE = PrintData Templates Overlay
-EMIT = Templates Auth
-APPLY = Overlay PackageManager Overlay SystemHarden TweakStartup RestartServices Finalize
-CLEAN = Overlay PackageManager
-
-#
-# First boot profile that corresponds to the Full profile above
-#
-[FirstBootFullWithPKGMGR]
-DISCOVERY/populate = DryRun SystemInfo
-DISCOVERY/policy-selection = DescendOrder
-PARSE/complete = Auth Interpolate PackageManager
-PARSE/initialize = PackageManager
-PREPARE = PrintData Templates Overlay
-EMIT = Templates Auth
-APPLY = Overlay PackageManager Overlay SystemHarden TweakStartup Finalize
-CLEAN = Overlay
-
#
# Actions
Modified: spine/branches/spine_2_2-devel/tests/Helpers/Data.pm
==============================================================================
--- spine/branches/spine_2_2-devel/tests/Helpers/Data.pm Tue Mar 30 08:35:44 2010 (r317)
+++ spine/branches/spine_2_2-devel/tests/Helpers/Data.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -1,28 +1,116 @@
+# -*- mode: cperl; cperl-continued-brace-offset: -4; indent-tabs-mode: nil; -*-
+# vim:shiftwidth=2:tabstop=8:expandtab:textwidth=78:softtabstop=4:ai:
+
+# $Id$
+
+#
+# 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.
+#
+# 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/>.
+#
+# (C) Copyright Ticketmaster, Inc. 2007
+#
+
package Helpers::Data;
use Spine::Data;
use Spine::Registry;
+use Spine::Constants qw(:basic);
use File::Spec::Functions;
my %configs = (
- "basic" => {
- spine => { Profile => "fake_profile" },
- fake_profile => { TestPoint => "TestCase" },
- },
-);
+ "basic" => { spine => { Profile => "fake_profile" },
+ fake_profile => { TestPoint => "TestCase" }, },
+ "parselets" => {
+ spine => { Profile => "parselet_profile" },
+ parselet_profile => {
+ "PARSE/key" => "Parselet::Basic"
+ . " Parselet::Complex"
+ . " Parselet::Operator"
+ . " Parselet::Dynamic"
+ . " Templates",
+ "PARSE/key/line" => "Parselet::Operator",
+ "PARSE/key/complex" => "Parselet::JSON Parselet::Dynamic",
+ "PARSE/key/dynamic" => "Parselet::DNS", }, }, );
+
+# a profile with the core plugins loaded
+$configs{core_plugins} = { spine => { Profile => "std_profile" },
+ std_profile => {
+ "DISCOVERY/policy-selection" => "DescendOrder",
+ "DISCOVERY/populate" => "DescendOrder",
+ %{ $configs{parselets}->{parselet_profile} } }
+ };
+
+sub load_plugins {
+ my ( $data, $registry ) = @_;
+
+ my $config = $data->getkey('c_config');
+ my $profile = $config->{spine}->{Profile};
+
+ while ( my ( $phase, $plugins ) = each( %{ $config->{$profile} } ) ) {
+ my @plugins = split( /(?:\s*,?\s+)/, $plugins );
+ foreach (@plugins) {
+ unless ( $registry->load_plugin($_) == SPINE_SUCCESS ) {
+ print STDERR "$phase: Failed to load ($_)!\n";
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+# Allow more plugins to be loaded (fake the config some more)
+sub add_plugin {
+ my ( $data, $reg, $hook_point, $plugin ) = @_;
+ my $config = $data->getkey('c_config');
+ my $profile = $config->{spine}->{Profile};
+ if (exists $config->{$profile}->{$hook_point}) {
+ $config->{$profile}->{$hook_point} .= " $plugin";
+ } else {
+ $config->{$profile}->{$hook_point} = "$plugin";
+ }
+
+}
+
+# Load all hooks in a plugin automagically
+# this beeing needed make me think we really need to redo reg
+sub auto_load_plugin {
+ my ($data, $reg, $plugin) = @_;
+ # I know this may die, but that ok this is a test!
+ $reg->load_plugin($plugin) || die("Could not load $plugin");
+ # an magically load all hooks
+ $plug = $reg->find_plugin($plugin);
+ my $plug = $reg->{PLUGINS}->{$plug};
+ foreach (keys %{$plug->{hooks}}) {
+ add_plugin($data, $reg, $_, $plugin);
+ $point = $reg->get_hook_point($_);
+ $point->install_hook($plugin, $plug);
+ }
+ load_plugins($data, $reg);
+}
sub new_data_obj {
- my $userconf = shift || "basic";
- my $croot = shift || "test_root";
+ my $userconf = shift || "basic";
+ my $croot = shift || "test_root";
+
+ my $conf = $configs{$userconf};
+
+ my $reg = new Spine::Registry($conf);
+
+ my $data = Spine::Data->new( croot => $croot,
+ config => $conf,
+ release => 1 );
+
+ return undef unless load_plugins( $data, $reg );
- my $conf = $configs{$userconf};
-
- my $reg = new Spine::Registry($conf);
-
- my $data = Spine::Data->new( croot => "test_root",
- config => $conf,
- release => 1);
-
- return $data, $reg;
+ return $data, $reg;
}
-1;
\ No newline at end of file
+1;
Added: spine/branches/spine_2_2-devel/tests/Helpers/DescendOrder.pm
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ spine/branches/spine_2_2-devel/tests/Helpers/DescendOrder.pm Tue Mar 30 08:39:16 2010 (r318)
@@ -0,0 +1,42 @@
+# -*- mode: cperl; cperl-continued-brace-offset: -4; indent-tabs-mode: nil; -*-
+# vim:shiftwidth=2:tabstop=8:expandtab:textwidth=78:softtabstop=4:ai:
+
+# $Id$
+
+#
+# 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.
+#
+# 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/>.
+#
+# (C) Copyright Ticketmaster, Inc. 2007
+#
+
+
+# small wrapper for loading descend order for testing descend plugins
+package Helpers::DescendOrder;
+use Helpers::Data;
+
+sub init {
+ my ($data, $reg) = @_;
+ Helpers::Data::auto_load_plugin($data, $reg, "DescendOrder");
+ my $point = $reg->get_hook_point("DISCOVERY/populate");
+ $point->run_hooks($data);
+
+}
+
+sub run {
+ my ($data, $reg) = @_;
+ my $point = $reg->get_hook_point("DISCOVERY/policy-selection");
+ $point->run_hooks($data);
+
+}
+
+1;
\ No newline at end of file
Modified: spine/branches/spine_2_2-devel/tests/Makefile
==============================================================================
--- spine/branches/spine_2_2-devel/tests/Makefile Tue Mar 30 08:35:44 2010 (r317)
+++ spine/branches/spine_2_2-devel/tests/Makefile Tue Mar 30 08:39:16 2010 (r318)
@@ -18,7 +18,18 @@
#
# (C) Copyright Ticketmaster, Inc. 2007
#
+
+# Attempt to run tests in a useful order...
+# Modules (not plugins)
+tests = spine-mod-*.t
+# Test the core parts of spineof spine
+tests += spine-core.t spine-core-*.t
+# Plugin tests (core plugins then other plugins)
+tests += spine-coreplug-*.t spine-plug-*.t
+
all:
- PERL5LIB=../lib perl -MTest::Harness -e '$$Test::Harness::verbose=0; runtests @ARGV;' *.t
+ PERL5LIB=../lib perl -MTest::Harness -e \
+ '$$Test::Harness::verbose=0; runtests @ARGV;' $(tests)
verbose:
- PERL5LIB=../lib perl -MTest::Harness -e '$$Test::Harness::verbose=1; runtests @ARGV;' *.t
+ PERL5LIB=../lib perl -MTest::Harness -e \
+ '$$Test::Harness::verbose=1; runtests @ARGV;' $(tests)
Added: spine/branches/spine_2_2-devel/tests/spine-core-parselets.t
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ spine/branches/spine_2_2-devel/tests/spine-core-parselets.t Tue Mar 30 08:39:16 2010 (r318)
@@ -0,0 +1,79 @@
+#!/usr/bin/perl -w
+# -*- mode: cperl; cperl-continued-brace-offset: -4; indent-tabs-mode: nil; -*-
+# vim:shiftwidth=2:tabstop=8:expandtab:textwidth=78:softtabstop=4:ai:
+
+# $Id$
+
+#
+# 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.
+#
+# 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/>.
+#
+# (C) Copyright Ticketmaster, Inc. 2007
+#
+use strict;
+
+#
+# Test that Spine::Registry + Spine::Data works as expected
+use Test::More qw(no_plan);
+use Spine::Data;
+use Spine::Key;
+use Helpers::Data;
+use Spine::Constants qw(:basic :plugin);
+
+my ( $data, $reg ) = Helpers::Data::new_data_obj("parselets");
+
+
+# parse the key
+# HOOKME Parselet expansion
+my $registry = new Spine::Registry;
+my $point = $registry->get_hook_point('PARSE/key');
+
+my $obj = new Spine::Key("test data\ntest data");
+$obj->metadata_set( keyname => "example_key",
+ description => "example_key key" );
+
+# Test basic scalar to array ref conversion (Spine::Parselet::Basic)
+my $rc;
+$obj = $data->read_key($obj);
+is( join( " ", @{ $obj->get() } ), "test data test data", "key data" )
+;
+# did it also make it into the tree in a hidden way?
+is( join( " ", @{ $data->getvals("example_key") } ),
+ "test data test data",
+ "key data made it into Spine::Data" );
+
+
+# Test that basic sclars are added to the array (Spine::Parselet::Operator)
+$obj->set("some more data");
+$obj = $data->read_key($obj, ["initial data"]);
+is( join( " ", @{ $obj->get() } ),
+ "initial data some more data",
+ "key data after merge" );
+# did it again make it into Spine::Data
+is( join( " ", @{ $data->getvals("example_key") } ),
+ "initial data some more data",
+ "key data after merge is in Spine::Data" );
+
+# Now lets test the remove syntax (both new and old style)
+$obj->set("more\ndata\n-Some\nspine_remove(Silly)\nand me");
+$obj = $data->read_key($obj, [ "Some", "Silly", "Data" ]);
+is( join( " ", @{ $obj->get() } ),
+ "Data more data and me",
+ "key data after remove" );
+
+$obj->set("=\nJust Me");
+$obj = $data->read_key($obj, ["old data"]);
+
+# check a json key
+$obj->set("#%JSON\n{ foo: 'baa' }\n");
+$obj = $data->read_key($obj);
+is( $obj->get()->{foo}, "baa", "JSON/Complex decoded" );
Modified: spine/branches/spine_2_2-devel/tests/spine-core.t
==============================================================================
--- spine/branches/spine_2_2-devel/tests/spine-core.t Tue Mar 30 08:35:44 2010 (r317)
+++ spine/branches/spine_2_2-devel/tests/spine-core.t Tue Mar 30 08:39:16 2010 (r318)
@@ -30,8 +30,9 @@
my ($data, $reg) = Helpers::Data::new_data_obj();
isa_ok($data, "Spine::Data");
-# Attempt to load the Spine::Plugin::TestCase
-is($reg->load_plugin("TestCase"), SPINE_SUCCESS, "register plugin");
+#### XXX: now done withing Helpers::Data
+#### Attempt to load the Spine::Plugin::TestCase
+###is($reg->load_plugin("TestCase"), SPINE_SUCCESS, "register plugin");
# Attempt to create a hook point
ok($reg->create_hook_point("TestPoint"), "create hook point");
@@ -51,3 +52,15 @@
is($rc, PLUGIN_SUCCESS, "run_hooks_until (rc)");
is($errs, 0, "run_hooks_until (error count)");
is($results->[0]->[0], "test", "run_hooks_until (name return)");
+
+# does key hiding work?
+use Spine::Key;
+my $obj = new Spine::Key("test");
+$data->set("my_key", $obj);
+# Does Spine::Data hide the fact that my_key is a Spine::Key?
+is($data->getval("my_key"), "test", "Hidden Spine::Key within Spine::Data");
+# Does Spine::Data::getkey reveal the key
+is($data->getkey("my_key")->get(), "test",
+ "Hidden Spine::Key reveald by Spine::Data::getkey");
+
+
Added: spine/branches/spine_2_2-devel/tests/spine-coreplug-DescendOrder.t
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ spine/branches/spine_2_2-devel/tests/spine-coreplug-DescendOrder.t Tue Mar 30 08:39:16 2010 (r318)
@@ -0,0 +1,76 @@
+#!/usr/bin/perl -w
+# -*- mode: cperl; cperl-continued-brace-offset: -4; indent-tabs-mode: nil; -*-
+# vim:shiftwidth=2:tabstop=8:expandtab:textwidth=78:softtabstop=4:ai:
+
+# $Id$
+
+#
+# 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.
+#
+# 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/>.
+#
+# (C) Copyright Ticketmaster, Inc. 2007
+#
+use strict;
+
+#
+# Test that Spine::Plugin::DescendOrder works as expected
+
+use Test::More qw(no_plan);
+use Helpers::Data;
+use File::Spec::Functions;
+use Spine::Constants qw(:basic :plugin :keys);
+
+# create a fake data obj using the basic profile
+my ( $data, $reg ) = Helpers::Data::new_data_obj();
+
+print "Testing Spine::Plugin::DescendOrder\n";
+
+use Spine::Plugin::DescendOrder;
+
+# does it create it's magic key ok?
+Spine::Plugin::DescendOrder::init($data);
+my $hkey = $data->getkey(SPINE_HIERARCHY_KEY);
+isa_ok( $hkey, "Spine::Plugin::DescendOrder::Key" );
+
+#can I add something?
+$hkey->merge( { uri => "fake://little/uri/1",
+ name => "uri1" } );
+my @items = $hkey->resolv_order();
+is( scalar(@items), 1, "One item added to the SPINE_HIERARCHY_KEY" );
+is( $items[0]->{name}, "uri1", "One correct item" );
+
+# can I add something else
+$hkey->merge( { uri => "fake://little/uri/2",
+ name => "uri2" } );
+@items = $hkey->resolv_order();
+is( scalar(@items), 2, "Second item added to the SPINE_HIERARCHY_KEY" );
+is( $items[0]->{name} . " " . $items[1]->{name},
+ "uri1 uri2", "Two correct items" );
+
+# can I add an item that succeds the first item and preceds the second
+$hkey->merge( { uri => "fake://little/uri/3",
+ name => "uri3",
+ dependencies => { precedes => "uri2" } },
+ $items[0] );
+@items = $hkey->resolv_order();
+is( scalar(@items), 3, "Third item added to the SPINE_HIERARCHY_KEY" );
+is( $items[0]->{name} . " " . $items[1]->{name} . " " . $items[2]->{name},
+ "uri1 uri3 uri2",
+ "Two correct items" );
+
+# now siince uri3 succedes (depends on) uri1, does remocving uri1 remove uri3
+# as it should?
+$hkey->remove("uri1");
+@items = $hkey->resolv_order();
+is( scalar(@items), 1, "One item left in SPINE_HIERARCHY_KEY" );
+is( $items[0]->{name}, "uri2", "Dependancies removed cleanly" );
+
Added: spine/branches/spine_2_2-devel/tests/spine-coreplug-Overlay.t
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ spine/branches/spine_2_2-devel/tests/spine-coreplug-Overlay.t Tue Mar 30 08:39:16 2010 (r318)
@@ -0,0 +1,109 @@
+#!/usr/bin/perl -w
+# -*- mode: cperl; cperl-continued-brace-offset: -4; indent-tabs-mode: nil; -*-
+# vim:shiftwidth=2:tabstop=8:expandtab:textwidth=78:softtabstop=4:ai:
+# $Id$
+#
+# 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.
+#
+# 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/>.
+#
+# (C) Copyright Ticketmaster, Inc. 2007
+#
+use strict;
+
+#
+# Test that Spine::Plugin::Overlay works as expected
+
+use Test::More qw(no_plan);
+use Helpers::Data;
+use Helpers::DescendOrder;
+use File::Spec::Functions;
+use Spine::Constants qw(:basic :plugin :keys);
+
+# create a fake data obj using the basic profile
+my ( $data, $reg ) = Helpers::Data::new_data_obj();
+
+print "Testing Spine::Plugin::Overlay\n";
+
+use Spine::Plugin::Overlay;
+
+# create some dirs
+ok( my $tmp_dir = File::Spec->tmpdir(), "get a tmp dir" );
+my $test_dir = catdir( $tmp_dir, "spine_tests");
+is( Spine::Util::makedir($test_dir), $test_dir, "makedir ($test_dir)" );
+my $overlay_src = catdir( $test_dir, "overlay");
+is( Spine::Util::makedir($overlay_src), $overlay_src, "makedir ($overlay_src)" );
+my $overlay_dst = catdir( $test_dir, "overlay-dst");
+is( Spine::Util::makedir($overlay_dst), $overlay_dst, "makedir ($overlay_dst)" );
+
+$data->set( "c_tmpdir", $test_dir );
+$data->set( "c_tmplink", File::Spec->catdir( $tmp_dir, "spine-link" ) );
+
+# does it create it's magic key ok?
+Spine::Plugin::Overlay::init($data);
+my $okey = $data->getkey(SPINE_OVERLAY_KEY);
+isa_ok( $okey, "Spine::Plugin::Overlay::Key" );
+
+# add an overlay
+$okey->merge( { uri => "some://uri/path",
+ name => "test" } );
+
+# get the bound overlays (hopefully zero)
+my $items = $okey->get_bound();
+is( scalar(@$items), 0, "No bound items" );
+
+# bind an overlay
+$okey->merge( { name => "test",
+ bind => "/somewhere" } );
+$items = $okey->get_bound();
+is( scalar(@$items), 1, "One bound item" );
+
+# add and bind in one (using simple syntax)
+$okey->merge("another://uri/location");
+$items = $okey->get_bound();
+is( scalar(@$items), 2, "Two bound item" );
+
+#check that they have the correct data
+is( join( " ", map { $_->{name} } @$items ),
+ "test another://uri/location",
+ "Correct names" );
+
+#check that they have the correct data
+is( join( " ", map { $_->{path} } @$items ), "/somewhere /", "Correct paths" );
+
+
+# Some tests require data from DescendOrder
+Helpers::DescendOrder::init($data, $reg);
+
+# test that load_overlays runs. Note that it does very little since
+# no plugins are loaded in the PREPARE/Overlay/load hook point
+is( Spine::Plugin::Overlay::load_overlays($data),
+ PLUGIN_SUCCESS, "load_overlays" );
+
+
+# test build_overlay. Not going to do much as no plugins are registers
+is( Spine::Plugin::Overlay::build_overlay($data),
+ PLUGIN_NOHOOKS, "build_overlays" );
+
+# clear out all out test overlays
+$okey->clear();
+
+# TODO test out the rest of the overlay code in this kind of order...
+# sync_attribs, find_changed, apply_overlay, clean_overlay, remove_tmpdir
+## create some content
+#open(FILE, ">" . catdir($overlay_src, "somefile"));
+#print FILE "Test Data\n";
+#close(FILE);
+#
+## bind an overlay
+#$okey->merge( { name => "test overlay",
+# uri => "",
+# bind => "/somewhere" } );
Copied: spine/branches/spine_2_2-devel/tests/spine-mod-chain.t (from r305, spine/branches/spine_2_2-devel/tests/spine-chain.t)
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ spine/branches/spine_2_2-devel/tests/spine-mod-chain.t Tue Mar 30 08:39:16 2010 (r318, copy of r305, spine/branches/spine_2_2-devel/tests/spine-chain.t)
@@ -0,0 +1,218 @@
+#!/usr/bin/perl -w
+# -*- mode: cperl; cperl-continued-brace-offset: -4; indent-tabs-mode: nil; -*-
+# vim:shiftwidth=2:tabstop=8:expandtab:textwidth=78:softtabstop=4:ai:
+
+# $Id$
+
+#
+# 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.
+#
+# 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/>.
+#
+# (C) Copyright Ticketmaster, Inc. 2007
+#
+use strict;
+use constant TIMEOUT => 50;
+use constant TEST_SIZE => 1000;
+use Spine::Constants qw(:basic :chain);
+
+
+# These will be called if there are missing requires or loops during tests
+my %issue_cbs = (
+ missing_cb => [
+ sub {
+ print STDERR "ERR: ($_[0]) requires missing ($_[1])\n";
+ }
+ ],
+ loop_cb => [
+ sub {
+ print STDERR "ERR: loop detected " . join( "<-", @{$_[0]} ), "\n";
+ }
+ ] );
+
+#
+# Test that Spine::Chain works as expected
+use Test::More qw(no_plan);
+
+# Check we can use the module
+BEGIN { use_ok('Spine::Chain'); }
+require_ok('Spine::Chain');
+
+my $chain = new Spine::Chain(%issue_cbs);
+isa_ok( $chain, "Spine::Chain" );
+
+my $item = 0;
+
+# Test that we can add one item
+is( $chain->add( name => "item_" . ++$item,
+ data => "item $item" ),
+ SPINE_SUCCESS,
+ "add one chain item" );
+
+# Test that we can add one item
+is( $chain->add( data => "item data" ),
+ SPINE_FAILURE, "correct error when adding a bad item" );
+
+# Test we cna add anohter 999 items
+while ( $item < TEST_SIZE ) {
+ $chain->add( name => "item_" . ++$item,
+ data => "item $item" ) == SPINE_SUCCESS
+ or last;
+}
+is( $item, TEST_SIZE, "add " . ( TEST_SIZE - 1 ) . " items to the chain" );
+
+my @items;
+
+# Check that head returns 1000 items (as array then as hash)
+eval {
+ $SIG{ALRM} = sub { die "took too long" };
+ alarm(TIMEOUT);
+ @items = $chain->head();
+ alarm(0);
+};
+ok( !$@, "call head (as array) within " . TIMEOUT . " seconds, $@" );
+is( scalar(@items), TEST_SIZE, "head returns " . TEST_SIZE . " items" );
+is( ref( $chain->head() ), "HASH", "call head (as scalar)" );
+
+# Check that the order is the same since no extra order info
+# was given i.e. tsort didn't reverse things
+my $p = 0;
+foreach (@items) {
+ $p++;
+ last if ( $_ ne "item $p" );
+}
+is( $p, TEST_SIZE, "insert order for " . TEST_SIZE . " items" );
+
+# Test succedes
+$chain = new Spine::Chain(%issue_cbs);
+$chain->add( name => "test_1", data => "item 1" );
+$chain->add( name => "test_2",
+ succedes => [ "test_4", "test_3" ],
+ data => "item 2" );
+$chain->add( name => "test_3", data => "item 3" );
+$chain->add( name => "test_4", data => "item 4" );
+$chain->add( name => "test_5", data => "item 5" );
+$chain->add( name => "test_6",
+ succedes => ["test_1"],
+ data => "item 6" );
+is( join( ":", ( $chain->head() ) ),
+ "item 1:item 4:item 3:item 2:item 5:item 6",
+ "succedes ordering" );
+
+# Test precedess
+$chain = new Spine::Chain(%issue_cbs);
+$chain->add( name => "test_0", data => "item 0" );
+$chain->add( name => "test_1", data => "item 1" );
+$chain->add( name => "test_2",
+ succedes => [ "test_4", "test_3" ],
+ data => "item 2" );
+$chain->add( name => "test_3", data => "item 3" );
+$chain->add( name => "test_4", data => "item 4" );
+$chain->add( name => "test_5", data => "item 5" );
+$chain->add( name => "test_6",
+ precedes => [ "test_1", "test_3" ],
+ data => "item 6" );
+is( join( ":", ( $chain->head() ) ),
+ "item 0:item 6:item 1:item 4:item 3:item 2:item 5",
+ "precedess+succedes ordering" );
+
+# Test Rules
+$chain = new Spine::Chain(%issue_cbs);
+
+# Create an order so items that provide SOMETHING_BEFORE come before
+# SOMETHING with comes before SOMETHING_AFTER
+$chain->add_provide_rule( provide => "SOMETHING",
+ succedes => ["SOMETHING_BEFORE"],
+ precedes => ["SOMETHING_AFTER"] );
+$chain->add_provide_rule( provide => "SOMETHING_AFTER",
+ succedes => [ "SOMETHING", "SOMETHING_BEFORE" ] );
+
+$chain->add( name => "test_0",
+ data => "item 0",
+ provides => ["SOMETHING_AFTER"] );
+$chain->add( name => "test_1", data => "item 1", provides => ["SOMETHING"] );
+$chain->add( name => "test_2",
+ succedes => [ "test_4", "test_3" ],
+ data => "item 2",
+ provides => ["SOMETHING"] );
+$chain->add( name => "test_3", data => "item 3", provides => ["SOMETHING"] );
+$chain->add( name => "test_4", data => "item 4", provides => ["SOMETHING"] );
+$chain->add( name => "test_5", data => "item 5", provides => ["SOMETHING"] );
+$chain->add( name => "test_6",
+ precedes => [ "test_1", "test_3" ],
+ data => "item 6",
+ provides => ["SOMETHING"] );
+$chain->add( name => "test_7",
+ data => "item 7",
+ provides => ["SOMETHING_BEFORE"] );
+is( join( ":", ( $chain->head() ) ),
+ "item 7:item 6:item 1:item 4:item 3:item 2:item 5:item 0",
+ "precedess+succedes+rule ordering" );
+
+# provides and requires
+$chain = new Spine::Chain(%issue_cbs);
+$chain->add_provide_rule( provide => "SOMETHING",
+ succedes => ["SOMETHING_BEFORE"],
+ precedes => ["SOMETHING_AFTER"] );
+$chain->add_provide_rule( provide => "SOMETHING_AFTER",
+ succedes => [ "SOMETHING", "SOMETHING_BEFORE" ] );
+
+$chain->add( name => "test_0",
+ data => "item 0",
+ provides => ["SOMETHING_AFTER"] );
+$chain->add( name => "test_1", data => "item 1", provides => ["SOMETHING"] );
+$chain->add( name => "test_2",
+ succedes => [ "test_4", "test_3" ],
+ data => "item 2",
+ provides => [ "SOMETHING", ] );
+$chain->add( name => "test_3", data => "item 3", provides => ["SOMETHING"] );
+$chain->add( name => "test_4", data => "item 4", provides => ["SOMETHING"] );
+$chain->add( name => "test_5",
+ data => "item 5",
+ provides => ["SOMETHING"],
+ requires => ["foo"] );
+$chain->add( name => "test_6",
+ precedes => [ "test_1", "test_3" ],
+ data => "item 6",
+ provides => ["SOMETHING"] );
+$chain->add( name => "test_7",
+ data => "item 7",
+ provides => ["SOMETHING_BEFORE"] );
+$chain->add( name => "test_8",
+ data => "item 8",
+ provides => [ "SOMETHING", "foo" ] );
+is( join( ":", ( $chain->head() ) ),
+ "item 7:item 6:item 1:item 4:item 3:item 2:item 8:item 5:item 0",
+ "precedess+succedes+rule+provides+requires ordering" );
+
+# Test merge_deps
+$chain = new Spine::Chain( merge_deps => 1,
+ %issue_cbs );
+$chain->add( name => "test_1", data => "item 1" );
+$chain->add( name => "test_2", data => "item 2" );
+$chain->add( name => "test_3", data => "item 3", precedes => ["test_2"] );
+is( join( ":", ( $chain->head() ) ), "item 1:item 3:item 2", "pre merge test" );
+$chain->add( name => "test_3",
+ data => "item 3 updated",
+ precedes => ["test_1"] );
+is( join( ":", ( $chain->head() ) ),
+ "item 3 updated:item 1:item 2",
+ "post merge test" );
+
+# Test remove_orphans
+$chain = new Spine::Chain( remove_orphans => 1,
+ %issue_cbs );
+$chain->add( name => "test_0", data => "item 0" );
+$chain->add( name => "test_1", data => "item 1", succedes => ["foo"] );
+$chain->add( name => "test_2", data => "item 2", succedes => ["test_1"] );
+$chain->add( name => "test_3", data => "item 3", succedes => ["test_2"] );
+$chain->add( name => "test_4", data => "item 4", succedes => ["test_0"] );
+is( join( ":", ( $chain->head() ) ), "item 0:item 4", "orphan removal" );
Added: spine/branches/spine_2_2-devel/tests/spine-mod-key.t
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ spine/branches/spine_2_2-devel/tests/spine-mod-key.t Tue Mar 30 08:39:16 2010 (r318)
@@ -0,0 +1,69 @@
+#!/usr/bin/perl -w
+# -*- mode: cperl; cperl-continued-brace-offset: -4; indent-tabs-mode: nil; -*-
+# vim:shiftwidth=2:tabstop=8:expandtab:textwidth=78:softtabstop=4:ai:
+
+# $Id$
+
+#
+# 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.
+#
+# 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/>.
+#
+# (C) Copyright Ticketmaster, Inc. 2007
+#
+use strict;
+
+#
+# Test that Spine::Util works as expected
+use Test::More qw(no_plan);
+use Spine::Constants qw(:basic);
+use Spine::Key;
+
+# test array code
+my $key_obj = new Spine::Key( [ 'one', 'two', 'three' ] );
+
+isa_ok( $key_obj, "Spine::Key" );
+
+is( join( " ", @{ $key_obj->get() } ),
+ "one two three",
+ "Spine::Key->get() ARRAY" );
+
+$key_obj->set( [ 'one', 'two' ] );
+is( join( " ", @{ $key_obj->get() } ), "one two", "Spine::Key->set() ARRAY" );
+
+$key_obj->merge( ['three'] );
+is( join( " ", @{ $key_obj->get() } ),
+ "one two three",
+ "Spine::Key->merge() ARRAY" );
+
+$key_obj->merge( ['zero'], { reverse => 1 } );
+is( join( " ", @{ $key_obj->get() } ),
+ "zero one two three",
+ "Spine::Key->merge(reverse) ARRAY" );
+$key_obj->remove("o");
+is( join( " ", @{ $key_obj->get() } ), "three", "Spine::Key->remove() ARRAY" );
+
+$key_obj = new Spine::Key( [ 'one', 'two', 'three' ] );
+$key_obj->keep("o");
+is( join( " ", @{ $key_obj->get() } ), "one two", "Spine::Key->keep() ARRAY" );
+
+# Test hash code
+$key_obj = new Spine::Key( { one => 1, two => 2, three => 3 } );
+
+isa_ok( $key_obj, "Spine::Key" );
+
+is( ref( $key_obj->get() ), "HASH", "Spine::Key->get() HASH" );
+
+$key_obj->merge( { four => 4, five => 5 } );
+my $result = $key_obj->get();
+is( $result->{four} . $result->{five} . $result->{one},
+ "451", "Spine::Key->merge() HASH" );
+
Copied and modified: spine/branches/spine_2_2-devel/tests/spine-mod-util.t (from r310, spine/branches/spine_2_2-devel/tests/spine-util.t)
==============================================================================
--- spine/branches/spine_2_2-devel/tests/spine-util.t Fri Nov 20 08:02:19 2009 (r310, copy source)
+++ spine/branches/spine_2_2-devel/tests/spine-mod-util.t Tue Mar 30 08:39:16 2010 (r318)
@@ -20,6 +20,7 @@
# (C) Copyright Ticketmaster, Inc. 2007
#
use strict;
+
#
# Test that Spine::Util works as expected
use Test::More qw(no_plan);
@@ -27,102 +28,104 @@
use File::Spec::Functions;
use Spine::Constants qw(:basic);
-my ($data, $reg) = Helpers::Data::new_data_obj();
-isa_ok($data, "Spine::Data");
-
print "Testing Spine::Util\n";
+my ( $data, $reg ) = Helpers::Data::new_data_obj();
+isa_ok( $data, "Spine::Data" );
+
# Check we can use the module
-BEGIN { use_ok('Spine::Util');
- use_ok('Spine::Util::Exec') }
+BEGIN {
+ use_ok('Spine::Util');
+ use_ok('Spine::Util::Exec');
+}
require_ok('Spine::Util');
require_ok('Spine::Util::Exec');
-ok(my $tmp_dir = File::Spec->tmpdir(), "get a tmp dir");
+ok( my $tmp_dir = File::Spec->tmpdir(), "get a tmp dir" );
-ok(chdir($tmp_dir), "change to tmp");
+ok( chdir($tmp_dir), "change to tmp" );
#Test mkdir_p
-my $dir = File::Spec->catdir($tmp_dir, "spine_tests", "something");
-ok(Spine::Util::mkdir_p($dir), "mkdir_p ($dir)");
-ok(chdir("spine_tests"), "chdir ($dir)");
+my $dir = File::Spec->catdir( $tmp_dir, "spine_tests", "something" );
+ok( Spine::Util::mkdir_p($dir), "mkdir_p ($dir)" );
+ok( chdir("spine_tests"), "chdir ($dir)" );
rmdir($dir);
# Test makedir
-$dir = catdir($tmp_dir, "spine_tests", "somethingelse");
-is(Spine::Util::makedir($dir), $dir, "makedir ($dir)");
+$dir = catdir( $tmp_dir, "spine_tests", "somethingelse" );
+is( Spine::Util::makedir($dir), $dir, "makedir ($dir)" );
rmdir($dir);
-
# Test find_exec
my $file_name = "examplefile";
-my $file = catfile($tmp_dir, "spine_tests", $file_name);
-open(FILE, ">$file");
+my $file = catfile( $tmp_dir, "spine_tests", $file_name );
+open( FILE, ">$file" );
close(FILE);
-ok(chmod(0755, "$file"), "created executable ($file_name)");
-ok(!Spine::Util::find_exec($data, $file_name), "find_exec error return");
-$data->{$file_name.Spine::Util::Exec::EXEC_KEY_EXTN} = $file;
-is(Spine::Util::find_exec($data, $file_name),
- $file, "find_exec " . $file_name . Spine::Util::Exec::EXEC_KEY_EXTN." key");
-delete $data->{$file_name.Spine::Util::Exec::EXEC_KEY_EXTN};
-$data->{+Spine::Util::Exec::COMPLEX_EXEC_KEY} = {$file_name => $file};
-is(Spine::Util::find_exec($data, $file_name),
- $file, "find_exec ". Spine::Util::Exec::COMPLEX_EXEC_KEY . " key");
-delete $data->{+Spine::Util::Exec::COMPLEX_EXEC_KEY};
-$data->{+Spine::Util::Exec::SPINE_PATH_KEY} = [catdir($tmp_dir, "spine_tests")];
-is(Spine::Util::find_exec($data, $file_name),
- $file, "find_exec ". +Spine::Util::Exec::SPINE_PATH_KEY . " key");
-delete $data->{+Spine::Util::Exec::SPINE_PATH_KEY};
-is(Spine::Util::find_exec($data, $file_name, "randomesomethign",
- catdir($tmp_dir, "spine_tests")),
- $file, "find_exec path as arg");
+ok( chmod( 0755, "$file" ), "created executable ($file_name)" );
+ok( !Spine::Util::find_exec( $data, $file_name ), "find_exec error return" );
+$data->{ $file_name . Spine::Util::Exec::EXEC_KEY_EXTN } = $file;
+is( Spine::Util::find_exec( $data, $file_name ),
+ $file,
+ "find_exec " . $file_name . Spine::Util::Exec::EXEC_KEY_EXTN . " key" );
+delete $data->{ $file_name . Spine::Util::Exec::EXEC_KEY_EXTN };
+$data->{ +Spine::Util::Exec::COMPLEX_EXEC_KEY } = { $file_name => $file };
+is( Spine::Util::find_exec( $data, $file_name ),
+ $file, "find_exec " . Spine::Util::Exec::COMPLEX_EXEC_KEY . " key" );
+delete $data->{ +Spine::Util::Exec::COMPLEX_EXEC_KEY };
+$data->{ +Spine::Util::Exec::SPINE_PATH_KEY } =
+ [ catdir( $tmp_dir, "spine_tests" ) ];
+is( Spine::Util::find_exec( $data, $file_name ),
+ $file, "find_exec " . +Spine::Util::Exec::SPINE_PATH_KEY . " key" );
+delete $data->{ +Spine::Util::Exec::SPINE_PATH_KEY };
+is( Spine::Util::find_exec( $data, $file_name, "randomesomethign",
+ catdir( $tmp_dir, "spine_tests" ) ),
+ $file,
+ "find_exec path as arg" );
unlink($file);
### Test Spine::Util::Exec
$data->{c_dryrun} = 0;
-my %config = (c => $data,
- exec => "printf",
- quiet => 1,
- args => [ 'test 1\ntest 2\ntest 3' ],
- inert => 0);
+my %config = ( c => $data,
+ exec => "printf",
+ quiet => 1,
+ args => ['test 1\ntest 2\ntest 3'],
+ inert => 0 );
my $exec_controler = Spine::Util::create_exec(%config);
-ok($exec_controler, "create a exec controler (Spine::Util::Exec)");
-ok($exec_controler->start(), "start: run command");
-is($exec_controler->lasterror(), undef, "command started");
+ok( $exec_controler, "create a exec controler (Spine::Util::Exec)" );
+ok( $exec_controler->start(), "start: run command" );
+is( $exec_controler->lasterror(), undef, "command started" );
#readlines
my @res = $exec_controler->readlines();
-is($res[0], "test 1\n", "readlines: check output line 1");
-is($res[1], "test 2\n", "readlines: check output line 2");
-is($res[2], "test 3", "readlines: check output line 3");
+is( $res[0], "test 1\n", "readlines: check output line 1" );
+is( $res[1], "test 2\n", "readlines: check output line 2" );
+is( $res[2], "test 3", "readlines: check output line 3" );
# readline
$exec_controler = Spine::Util::create_exec(%config);
-ok($exec_controler->start(), "start: run command");
-is($exec_controler->readline(), "test 1\n", "readline: check output line 1");
-is($exec_controler->readline(), "test 2\n", "readline: check output line 2");
-is($exec_controler->readline(), "test 3", "readline: check output line 3");
+ok( $exec_controler->start(), "start: run command" );
+is( $exec_controler->readline(), "test 1\n", "readline: check output line 1" );
+is( $exec_controler->readline(), "test 2\n", "readline: check output line 2" );
+is( $exec_controler->readline(), "test 3", "readline: check output line 3" );
# input
-$config{exec} = "cat";
-$config{args} = "-";
+$config{exec} = "cat";
+$config{args} = "-";
$exec_controler = Spine::Util::create_exec(%config);
-ok($exec_controler->start(), "start: run command");
-is($exec_controler->readline(1), undef, "readline: timeout test");
-ok($exec_controler->input("test text\n"),"input: send input");
-is($exec_controler->readline(), "test text\n", "readline: check output");
-ok($exec_controler->input("test more text\n"),"input: send input");
-is($exec_controler->readline(), "test more text\n", "readline: check output");
-ok($exec_controler->input("unfinished line"),"input: send input");
-is($exec_controler->readline(1), undef, "readline: timeout test");
-ok($exec_controler->isrunning(), "isrunning: check if it's still running");
-ok($exec_controler->closeinput(), "colseinput: close input");
-sleep(1);
-is($exec_controler->isrunning(), 0, "isrunning: check if it's now stopped");
-is($exec_controler->exitstatus(), 0, "exitstatus: did it exit cleanly");
-
-
+ok( $exec_controler->start(), "start: run command" );
+is( $exec_controler->readline(0.05), undef, "readline: timeout test" );
+ok( $exec_controler->input("test text\n"), "input: send input" );
+is( $exec_controler->readline(), "test text\n", "readline: check output" );
+ok( $exec_controler->input("test more text\n"), "input: send input" );
+is( $exec_controler->readline(), "test more text\n", "readline: check output" );
+ok( $exec_controler->input("unfinished line"), "input: send input" );
+is( $exec_controler->readline(0.05), undef, "readline: timeout test" );
+ok( $exec_controler->isrunning(), "isrunning: check if it's still running" );
+ok( $exec_controler->closeinput(), "colseinput: close input" );
+is( $exec_controler->wait(), 0, "wait: wait for it to finish" );
+is( $exec_controler->isrunning(), 0, "isrunning: check if it's now stopped" );
+is( $exec_controler->exitstatus(), 0, "exitstatus: did it exit cleanly" );
# Cleanup
-rmdir(catdir($tmp_dir, "spine_tests"));
+rmdir( catdir( $tmp_dir, "spine_tests" ) );
Added: spine/branches/spine_2_2-devel/tests/spine-plug-Descend_Disk.t
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ spine/branches/spine_2_2-devel/tests/spine-plug-Descend_Disk.t Tue Mar 30 08:39:16 2010 (r318)
@@ -0,0 +1,68 @@
+#!/usr/bin/perl -w
+# -*- mode: cperl; cperl-continued-brace-offset: -4; indent-tabs-mode: nil; -*-
+# vim:shiftwidth=2:tabstop=8:expandtab:textwidth=78:softtabstop=4:ai:
+
+# $Id$
+
+#
+# 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.
+#
+# 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/>.
+#
+# (C) Copyright Ticketmaster, Inc. 2007
+#
+use strict;
+
+#
+# Test that Spine::Plugin::DescendOrder works as expected
+
+use Test::More qw(no_plan);
+use Helpers::Data;
+use Helpers::DescendOrder;
+use File::Spec::Functions;
+use Spine::Constants qw(:basic :plugin :keys);
+use Spine::Plugin::Descend::Disk;
+
+# create a fake data obj using the "core_plugins" profile
+my ( $data, $reg ) = Helpers::Data::new_data_obj("core_plugins");
+# load out descend plugin
+Helpers::Data::auto_load_plugin($data, $reg, "Descend::Disk");
+# init descend order
+Helpers::DescendOrder::init($data, $reg);
+
+print "Testing Spine::Plugin::Descend::Disk\n";
+
+# does it hide the "include" key in the Spine::Data tree using a Blank key?
+Spine::Plugin::Descend::Disk::reserve_key($data);
+my $ikey = $data->getkey("include");
+isa_ok( $ikey, "Spine::Key::Blank" );
+
+# Attempt to reslove a disk based descend branch
+$data->set("policy_hierarchy", "file:///");
+
+# kick off descend order
+Helpers::DescendOrder::run($data, $reg);
+#Spine::Plugin::DescendOrder::create_order($data);
+
+# search for the items that should have been added
+# this also checks that Spine::Data still has the correct data.
+my @items = @{$data->getvals(SPINE_HIERARCHY_KEY)};
+my $found = 0;
+my $should_be_removed = 0;
+foreach (@items) {
+ $found++
+ if ( $_->{uri} eq "file:///config_group/test/"
+ || $_->{uri} eq "file:///config_group/second_test/" );
+ $should_be_removed++ if ( $_->{uri} eq "file:///config_group/to-remove/" );
+}
+is($found, 2, "Disk descend resolves as expected");
+is($should_be_removed, 0, "Removal later in the order works");
+
Added: spine/branches/spine_2_2-devel/tests/test_root/config/include
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ spine/branches/spine_2_2-devel/tests/test_root/config/include Tue Mar 30 08:39:16 2010 (r318)
@@ -0,0 +1,3 @@
+# used to test Spine::Plugin::Descend::Disk
+file:///config_group/to-remove/
+file:///config_group/test/
\ No newline at end of file
Added: spine/branches/spine_2_2-devel/tests/test_root/config_group/test/include
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ spine/branches/spine_2_2-devel/tests/test_root/config_group/test/include Tue Mar 30 08:39:16 2010 (r318)
@@ -0,0 +1,3 @@
+# used to test Spine::Plugin::Descend::Disk
+file:///config_group/second_test/
+-file:///config_group/to-remove/
\ No newline at end of file