Index: /trunk/Nebulous/Changes
===================================================================
--- /trunk/Nebulous/Changes	(revision 24280)
+++ /trunk/Nebulous/Changes	(revision 24281)
@@ -1,3 +1,6 @@
 Revision history for Nebulous
+
+0.17
+    - add chmod() API
 
 0.10
Index: /trunk/Nebulous/MANIFEST
===================================================================
--- /trunk/Nebulous/MANIFEST	(revision 24280)
+++ /trunk/Nebulous/MANIFEST	(revision 24281)
@@ -98,4 +98,5 @@
 t/66_client_xattr.t
 t/67_client_swap.t
+t/68_client_chmod.t
 t/70_neb-ls.t
 t/90_nebclient.t
Index: /trunk/Nebulous/lib/Nebulous/Client.pm
===================================================================
--- /trunk/Nebulous/lib/Nebulous/Client.pm	(revision 24280)
+++ /trunk/Nebulous/lib/Nebulous/Client.pm	(revision 24281)
@@ -9,5 +9,5 @@
 no warnings qw( uninitialized );
 
-our $VERSION = '0.09';
+our $VERSION = '0.17';
 
 use Digest::MD5;
@@ -1048,4 +1048,43 @@
 
 
+sub chmod
+{
+    my $self = shift;
+
+    my ($key, $mode) = validate_pos( @_,
+        {
+            type => SCALAR,
+        },
+        {
+            type => SCALAR,
+            regex       => qr/\d{3,4}/,
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    my $response = $self->{ 'server' }->chmod_object($key, $mode);
+    if ( $response->fault ) {
+        $self->set_err($response->faultstring);
+        if ($response->faultstring =~ /is valid object key/) {
+            $log->debug( "leaving" );
+            return;
+        }
+        if ($response->faultstring =~ /is allowable mode/) {
+            $log->debug( "leaving" );
+            return;
+        }
+
+        $log->logdie("unhandled fault - ", $self->err);
+    }
+
+    my $response_mode = $response->result;
+
+    $log->debug( "leaving" );
+
+    return $response_mode;
+}
+
+
 sub err
 {
Index: /trunk/Nebulous/lib/Nebulous/Client.pod
===================================================================
--- /trunk/Nebulous/lib/Nebulous/Client.pod	(revision 24280)
+++ /trunk/Nebulous/lib/Nebulous/Client.pod	(revision 24281)
@@ -13,10 +13,10 @@
     );
 
-    my $uri = $neb->create( "key", "node01" );
-    my $fh = $neb->open_create( "key", "node01" );
-    $neb->replicate( "key", "node01" );
-    $neb->cull( "key", "node01" );
-    $neb->lock( "key", 'write' );
-    $neb->unlock( "key", 'write' );
+    my $uri = $neb->create( $key, $vol );
+    my $fh = $neb->open_create( $key, $vol );
+    $neb->replicate( $key, $vol );
+    $neb->cull( $key, $vol );
+    $neb->lock( $key, 'write' );
+    $neb->unlock( $key, 'write' );
     $neb->setxattr( $key, $name, $value, $flags );
     $neb->getxattr( $key, $name );
@@ -24,14 +24,15 @@
     $neb->removexattr( $key, $name );
     my $uris = $neb->find_objects( $pattern );
-    my $uris = $neb->find_instances( "key", "node01" );
-    my $path = $neb->find( "key" );
-    my $fh = $neb->open( "key", 'read' );
-    $neb->delete( "key" );
-    $neb->copy( "key", "new_key", "node01" );
-    $neb->move( "key", "new_key" );
-    $neb->swap( "key1", "key2" );
+    my $uris = $neb->find_instances( $key, $vol );
+    my $path = $neb->find( $key );
+    my $fh = $neb->open( $key, 'read' );
+    $neb->delete( $key );
+    $neb->copy( $key, $new_key, $vol );
+    $neb->move( $key, $new_key );
+    $neb->swap( $key1, $key2 );
     $neb->delete_instance( $uri );
-    my $stats = $neb->stat( "key" );
+    my $stats = $neb->stat( $key );
     my $mounts = $neb->mounts();
+    my $mode = $neb->chmod( $key, $mode );
 
 =head1 DESCRIPTION
@@ -425,4 +426,11 @@
     ]
 
+=item * chmod( $key, $mode );
+
+Accepts 2 parameters, both mandatory.  C<$key> is the nebulous key to operate
+on and C<$mode> are the POSIX like file permissions to set on all instances of
+C<$key>.  C<$mode> must be specified in oct.  Returns C<$mode> or sucess or an
+exception on failure.
+
 =back
 
Index: /trunk/Nebulous/t/68_client_chmod.t
===================================================================
--- /trunk/Nebulous/t/68_client_chmod.t	(revision 24281)
+++ /trunk/Nebulous/t/68_client_chmod.t	(revision 24281)
@@ -0,0 +1,117 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2009  Joshua Hoblitt
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 13;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Nebulous::Util qw( :standard );
+use Test::URI;
+use Test::Nebulous;
+use URI;
+use File::stat;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+my $neb = Nebulous::Client->new(
+    proxy => "http://$hostport/nebulous",
+);
+
+Test::Nebulous->setup;
+
+# object with one instance
+{
+    my $key = "foo";
+    my $uri = $neb->create($key);
+
+    my $path = URI->new($uri)->path;
+    my $st1 = stat($path);
+
+    my $mode = $neb->chmod($key, 0440);
+    my $st2 = stat($path);
+
+    is($st2->mode &07777, 0440, "chmod() single instance");
+    is($mode, 0440, "returned mode");
+    is($neb->getxattr($key, 'user.mode'), 0440, "xattr user.mode");
+}
+
+Test::Nebulous->setup;
+
+# object with two instances
+{
+    my $key = "foo";
+    my $uri1 = $neb->create($key);
+    my $uri2 = $neb->replicate($key);
+
+    my $mode = $neb->chmod($key, 0440);
+
+    my $st1 = stat(URI->new($uri1)->path);
+    my $st2 = stat(URI->new($uri2)->path);
+
+    is($st1->mode &07777, 0440, "chmod() first instance");
+    is($st2->mode &07777, 0440, "chmod() second instance");
+    is($mode, 0440, "returned mode");
+    is($neb->getxattr($key, 'user.mode'), 0440, "xattr user.mode");
+}
+
+Test::Nebulous->setup;
+
+{
+    my $mode = $neb->chmod("foo", 0644);
+
+    is($mode, undef, "object does not exist");
+}
+
+Test::Nebulous->setup;
+
+{
+    $neb->create("foo");
+
+    my $mode = $neb->chmod("foo", 0640);
+
+    is($mode, undef, "mode is not 0440");
+}
+
+Test::Nebulous->setup;
+
+{
+    $neb->create("foo");
+
+    my $mode = $neb->chmod("foo", 999);
+
+    is($mode, undef, "bad mode");
+}
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->chmod();
+};
+like($@, qr/2 were expected/, "no params");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create("foo");
+
+    $neb->chmod("foo");
+};
+like($@, qr/2 were expected/, "one param");
+
+Test::Nebulous->setup;
+
+eval {
+    $neb->create("foo");
+
+    $neb->chmod("foo", 0440, 2);
+};
+like($@, qr/2 were expected/, "three params");
+
+Test::Nebulous->cleanup;
