Index: trunk/Nebulous/Changes
===================================================================
--- trunk/Nebulous/Changes	(revision 24939)
+++ trunk/Nebulous/Changes	(revision 24996)
@@ -8,4 +8,5 @@
     - add Nebulous::Client->prune() method
     - add Nebulous::Client->there_can_be_only_one() method
+    - add neb-cull --one_only flag
 
 0.10
Index: trunk/Nebulous/MANIFEST
===================================================================
--- trunk/Nebulous/MANIFEST	(revision 24939)
+++ trunk/Nebulous/MANIFEST	(revision 24996)
@@ -102,4 +102,5 @@
 t/70_neb-ls.t
 t/71_client_there_can_be_only_one.t
+t/72_neb-cull.t
 t/90_nebclient.t
 t/TEST.PL
Index: trunk/Nebulous/bin/neb-cull
===================================================================
--- trunk/Nebulous/bin/neb-cull	(revision 24939)
+++ trunk/Nebulous/bin/neb-cull	(revision 24996)
@@ -16,5 +16,5 @@
 use Pod::Usage qw( pod2usage );
 
-my ($server, $volume);
+my ($server, $volume, $one_only);
 
 $server = $ENV{'NEB_SERVER'} unless $server;
@@ -23,4 +23,5 @@
     'server|s=s'    => \$server,
     'volume|v=s'    => \$volume,
+    'one_only|o'    => \$one_only,
 ) || pod2usage( 2 );
 
@@ -40,10 +41,24 @@
     unless defined $neb;
 
+my @cull_args;    
+push @cull_args, $key;
 if (defined $volume) {
-    $neb->cull($key, $volume)
-        or die "failed to replicate Nebulous key: $key";
+    push @cull_args, $volume;
+}
+
+if ($one_only) {
+    my $status = $neb->there_can_be_only_one(@cull_args);
+    if ($status == 0) {
+        die "not enough instances of Nebulous key: $key";
+    }
+    if (not defined $status or $status < 1) {
+        die "failed to cull Nebulous key: $key - " . $neb->err
+            if defined $neb->err;
+        die "failed to cull Nebulous key: $key";
+    }
 } else {
-    $neb->cull($key)
-        or die "failed to replicate Nebulous key: $key";
+    unless (defined $neb->cull(@cull_args)) {
+        die "failed to cull Nebulous key: $key - " . $neb->err;
+    }
 }
 
@@ -58,5 +73,5 @@
 =head1 SYNOPSIS
 
-    neb-cull [--server <URL>] [--volume <volume name>] <key>
+    neb-cull [--server <URL>] [--volume <volume name>] [--one_only] <key>
 
 =head1 DESCRIPTION
@@ -77,4 +92,12 @@
 
 Symbolic name of the volume to create the new instance on.
+
+Optional.
+
+=item * --one_only|-o
+
+Removes all but one instances of an object.  Any inaccessible instances are
+removed.  If C<--volume> is specified, and an instance exists on that volume,
+all instances but the one on that volume are removed.
 
 Optional.
@@ -110,5 +133,5 @@
 =head1 COPYRIGHT
 
-Copyright (C) 2007-2008  Joshua Hoblitt.  All rights reserved.
+Copyright (C) 2007-2009  Joshua Hoblitt.  All rights reserved.
 
 This program is free software; you can redistribute it and/or modify it under
Index: trunk/Nebulous/lib/Nebulous/Client.pm
===================================================================
--- trunk/Nebulous/lib/Nebulous/Client.pm	(revision 24939)
+++ trunk/Nebulous/lib/Nebulous/Client.pm	(revision 24996)
@@ -297,5 +297,5 @@
         my $instances = $stats[6];
         if (defined $instances and $instances < 2) {
-            $log->debug("can not cull - not enough instances");
+            $self->set_err("can not cull - not enough instances");
             $log->debug("leaving");
 
@@ -305,5 +305,5 @@
         $locations = $self->find_instances($key, $vol_name);
         unless (defined $locations) {
-            $log->debug( "no instances" );
+            $self->set_err( "no instances" );
             $log->debug( "leaving" );
 
@@ -313,5 +313,5 @@
         $locations = $self->find_instances($key);
         unless ($locations) {
-            $log->debug("no instances");
+            $self->set_err( "no instances" );
             $log->debug("leaving");
 
@@ -320,5 +320,5 @@
 
         if (scalar @{ $locations } < 2) {
-            $log->debug("can not cull - not enough instances");
+            $self->set_err("can not cull - not enough instances");
             $log->debug("leaving");
 
@@ -365,6 +365,5 @@
         if (not defined $self->prune($key)) {
             # use the prune() method to also determine if $key is valid
-            $removed = undef;
-            return;
+            die "invalid key: $key";
         }
 
@@ -377,4 +376,7 @@
         if (defined $locations) {
             my $instances = $self->find_instances($key);
+            if (scalar @$instances < 2) {
+                die "not enough instances";
+            }
             foreach my $victim (@$instances) {
                 next if $victim eq $locations->[0];
@@ -387,4 +389,7 @@
             # start at one so cull() is called one less time then the # of
             # instances
+            if ($stats->[6] < 2) {
+                die "not enough instances";
+            }
             for (my $i = 1; $i < $stats->[6]; $i++) {
                 $self->cull($key);
@@ -395,4 +400,8 @@
     };
     if ($@) {
+        if ($@ =~ qr/invalid key/) {
+            $self->set_err($@);
+            return;
+        }
         $log->logdie($@);
     }
Index: trunk/Nebulous/t/72_neb-cull.t
===================================================================
--- trunk/Nebulous/t/72_neb-cull.t	(revision 24996)
+++ trunk/Nebulous/t/72_neb-cull.t	(revision 24996)
@@ -0,0 +1,181 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2009  Joshua Hoblitt
+
+=head1 NAME
+
+t/72_neb-cull.t - tests neb-cull
+
+=head1 SYNOPSIS
+    
+    prove t/72_neb-cull.t
+
+=cut
+
+use strict;
+use warnings;
+
+use Apache::Test qw( -withtestmore );
+plan tests => 29;
+
+use lib qw( ./lib ./t );
+
+use Test::Cmd;
+use Nebulous::Client;
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+my $neb_url  = "http://$hostport/nebulous";
+
+
+my $cmd = 'bin/neb-cull';
+
+# last ditch effort to make sure $cmd executable
+chmod 0755, $cmd;
+
+my $test = Test::Cmd->new(prog => $cmd, workdir => '');
+isa_ok($test, 'Test::Cmd');
+
+# NEB_SERVER env var not set
+undef $ENV{'NEB_SERVER'} if defined $ENV{'NEB_SERVER'};
+Test::Nebulous->setup;
+
+{
+    $test->run(args => '');
+    missing_args(2, "Required options: --server");
+}
+
+# NEB_SERVER set
+Test::Nebulous->setup;
+
+{
+    $ENV{NEB_SERVER} = $neb_url;
+
+    $test->run(args => '');
+    missing_args(2, "missing key operand");
+}
+
+Test::Nebulous->setup;
+
+{
+    my $key = 'foo';
+
+    my $neb = Nebulous::Client->new(
+        proxy => $neb_url,
+    );
+    $neb->create($key);
+
+    $test->run(args => $key);
+
+    is($neb->stat($key)->[6], 1, "correct # of instances");
+    is($? >> 8, 255, "exit code");
+    like($test->stdout, qr/^$/, "stdout");
+    like($test->stderr, qr/not enough instances/, "stderr");
+}
+
+Test::Nebulous->setup;
+
+{
+    my $key = 'foo';
+
+    my $neb = Nebulous::Client->new(
+        proxy => $neb_url,
+    );
+    $neb->create($key);
+    $neb->replicate($key);
+
+    $test->run(args => $key);
+
+    is($neb->stat($key)->[6], 1, "correct # of instances");
+    is($? >> 8, 0, "exit code");
+    like($test->stdout, qr/^$/, "stdout");
+    like($test->stderr, qr/^$/, "stderr");
+}
+
+Test::Nebulous->setup;
+
+{
+    my $key = 'foo';
+
+    my $neb = Nebulous::Client->new(
+        proxy => $neb_url,
+    );
+    $neb->create($key);
+    $neb->replicate($key);
+    $neb->replicate($key);
+
+    $test->run(args => $key);
+
+    is($neb->stat($key)->[6], 2, "correct # of instances");
+    is($? >> 8, 0, "exit code");
+    like($test->stdout, qr/^$/, "stdout");
+    like($test->stderr, qr/^$/, "stderr");
+}
+
+Test::Nebulous->setup;
+
+{
+    my $key = 'foo';
+
+    my $neb = Nebulous::Client->new(
+        proxy => $neb_url,
+    );
+    $neb->create($key);
+
+    $test->run(args => "--one_only " . $key);
+
+    is($neb->stat($key)->[6], 1, "correct # of instances");
+    is($? >> 8, 255, "exit code");
+    like($test->stdout, qr/^$/, "stdout");
+    like($test->stderr, qr/not enough instances/, "stderr");
+}
+
+Test::Nebulous->setup;
+
+{
+    my $key = 'foo';
+
+    my $neb = Nebulous::Client->new(
+        proxy => $neb_url,
+    );
+    $neb->create($key);
+    $neb->replicate($key);
+
+    $test->run(args => "--one_only " . $key);
+
+    is($neb->stat($key)->[6], 1, "correct # of instances");
+    is($? >> 8, 0, "exit code");
+    like($test->stdout, qr/^$/, "stdout");
+    like($test->stderr, qr/^$/, "stderr");
+}
+
+Test::Nebulous->setup;
+
+{
+    my $key = 'foo';
+
+    my $neb = Nebulous::Client->new(
+        proxy => $neb_url,
+    );
+    $neb->create($key);
+    $neb->replicate($key);
+    $neb->replicate($key);
+
+    $test->run(args => "--one_only " . $key);
+
+    is($neb->stat($key)->[6], 1, "correct # of instances");
+    is($? >> 8, 0, "exit code");
+    like($test->stdout, qr/^$/, "stdout");
+    like($test->stderr, qr/^$/, "stderr");
+}
+
+
+Test::Nebulous->cleanup;
+
+sub missing_args
+{
+    my ($exit, $errstr) = @_;
+
+    is($? >> 8, $exit, "error code is: $exit");
+    like($test->stderr, qr/$errstr/, "error string is: $errstr");
+}
