Group
Extension

Manta-Client/lib/Manta/Client.pm

package Manta::Client 0.6;

use strict;
use warnings;
use Carp 'croak';
use Crypt::OpenSSL::RSA;
use JSON::Parse 'parse_json';
use LWP::UserAgent;
use MIME::Base64 'encode_base64';
use Net::SSH::Perl::Key;

sub new {
	my $class = shift;
	croak "Illegal parameter list has odd number of values" if @_ % 2;
	my %params = @_;
	my $self = {};
	bless $self, $class;
	for my $required (qw{ user url key_file }) {
		croak "Required parameter '$required' not passed to '$class' constructor" unless defined $params{$required};  
		$self->{$required} = $params{$required};
	}
	return $self;
}

sub _request {
	my $self = shift;
	croak "Illegal parameter list has odd number of values" if @_ % 2;
	my %params = @_;
	for my $required (qw{ method path }) {
		croak "Required parameter '$required' not passed to _request method" unless defined $params{$required};  
	}
	my $date = scalar gmtime;
	my $date_header = "date: $date";
	open F, "<$self->{key_file}";
	my $key = join '', <F>;
	close F;
	my $fingerprint = Net::SSH::Perl::Key->read_private("RSA", $self->{key_file})->fingerprint("md5");
	my $privatekey = Crypt::OpenSSL::RSA->new_private_key($key);
	$privatekey->use_sha256_hash();
	my $signature = encode_base64($privatekey->sign($date_header), "");
	my $h = HTTP::Headers->new(%{$params{headers}});
	$h->header(date => $date);
	$h->header('Authorization' => "Signature keyId=\"/$self->{user}/keys/$fingerprint\",algorithm=\"rsa-sha256\",signature=\"$signature\"");
	my $ua = LWP::UserAgent->new(default_headers => $h);
	my $response;
	if ($params{method} eq "GET") {
		$response = $ua->get("$self->{url}/$params{path}");
		if ($response->is_success) {
			return $response->decoded_content;
		} else {
			return undef;
		}
	} elsif ($params{method} eq "PUT") {
		$response = $ua->put("$self->{url}/$params{path}", Content => $params{content}, %$h);
		return !!$response->is_success;
	} elsif ($params{method} eq "DELETE") {
		$response = $ua->delete("$self->{url}/$params{path}");
		return !!$response->is_success;
	} else {
		croak ("bad method");
	}
}

sub get {
	my ($self, $path) = @_;
	my $response = $self->_request(path => $path, method => "GET");
	return $response;
}

sub put {
	my $self = shift;
	croak "Illegal parameter list has odd number of values" if @_ % 2;
	my %params = @_;
	croak "Required parameter 'path' not passed to put method put" unless defined $params{path};
	return $self->_request(path => $params{path}, method => "PUT", content => $params{content}, headers => {"content-type" => $params{"content-type"}});
}

sub rm {
	my ($self, $path) = @_;
	my $response = $self->_request(path => $path, method => "DELETE");
	return $response;
}

sub mkdir {
	my ($self, $path) = @_;
	return $self->put(path => $path, "content-type" => "application/json; type=directory");
}

sub ls {
	# FIXME - limited to 256 objects
	my ($self, $path) = @_;
	my $response = $self->_request(path => $path, method => "GET");
	if ($response) {
		my %results;
		foreach(split '\n', $response) {
			my $json = parse_json($_);
			$results{$json->{name}} = { type => $json->{type}, mtime => $json->{mtime}, size => $json->{size}, etag => $json->{etag} };
		}
		return \%results;
	}
	return undef;
}

sub ln {
	my ($self, $src, $dst) = @_;
	return $self->_request(path => $dst, method => "PUT", headers => {Location => $src, "content-type" => "application/json; type=link"});
}

1;

__END__

=head1 NAME

Manta::Client - a Manta client implementation in Perl

=head1 SYNOPSIS

  my $manta = Manta::Client->new(user => $username,
    url => "https://us-east.manta.joyent.com",
    key_file => "/root/.ssh/id_rsa");
  my $object = $manta->get("/$username/stor/file.txt");
  $manta->put(path => "/$username/stor/file.txt",
    content => $content,
    "content-type" => "text/plain");
  $manta->rm("$username/stor/file.txt");
  $manta->mkdir("/$username/stor/new_directory");
  my $files = $manta->ls("$username/stor");

=head1 DESCRIPTION

Manta::Client communicates with some of the API endpoints defined at L<https://apidocs.joyent.com/manta/>.

=head1 CLASS METHODS

=head2 new

This is the constructor method. It requires a hash argument containing three elements: C<user> - the Manta username; C<url> - the URL of the Manta API endpoint; and C<key_file> - the path to an SSH private key file

=head2 get

Gets an object. It requires a single argument, the Manta path of the object to retrieve. It returns the contents of the object, or undef on failure.

=head2 put

Put an object. It requires a hash argument containing: C<path> - destination Manta path; C<content> - contents of the object to be uploaded; and C<content-type> (optional) - the MIME type of the object (defaults to application/octet-stream)

It returns true on success and false on failure.

=head2 rm

Destroy an object. It requires a single argument, the Manta path of the object to destroy. It returns true on success and false on failure.

=head2 mkdir

Create a directory. It requires a single argument, the Manta path of the directory to create. It returns true on success and false on failure.

=head2 ls

List contents of a directory. It requires a single argument, the Manta path of the directory to list. It returns a hashref (keying on the object path) of hashrefs (or undef on failure). Each hashref has the following keys: C<type> - the MIME type; C<mtime> - the modification time in YYYY-mm-ddTHH:MM:ss.sssZ format; C<size> - size of the object in bytes; and C<etag> - UUID of the object

=head2 ln

Create a snaplink. It requires two parameters, the path from the source object and the path to the new snaplink.

It returns true on success and false on failure.


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.