Amazon-S3-Lite

 view release on metacpan or  search on metacpan

ChangeLog  view on Meta::CPAN

	* release-notes/release-notes-1.2.2.md: new
	* release-notes.md: updated
	* VERSION: bump
	* lib/Amazon/S3/Lite.pm.in
	(new)
	- allow ref or list
	- make us-east-1 default region
	(init_logger)
	- set default log level to warn
	- set log_level
	(_init_credentials): refactored
	(log_level): new
	(_create_noitification_configuration)
	- use new filters template
	- set filter to q{} if no filter
	(__DATA__): +:filters
	* lib/Amazon/S3/Lite/Logger.pm.in
	(new)
	- accept log_level option
	- refactored
	(_log_level): new

ChangeLog  view on Meta::CPAN

	* release-notes-1.0.1.md: new
	* README.md: generated
	* VERSION: bump
	* .gitignore: CPAN::Maker::Bootstrapper build refactor
	* Makefile: likewise
	* docker-compose.yml: +logs
	* lib/Amazon/S3/Lite.pm.in
	- pod updates
	(_init_logger)
	- don't rain on someone's logger
	(_init_credentials)
	- awkward attempt reconcile Amazon::Credentials methods
	(_signer): ditto
	(_request): add x-amz-content-sha256 header
	* lib/Amazon/S3/Lite/Credentials.pm.in
	(session_token): alias for token()
	* requires
	- +IO::Socket::SSL, Net::SSLeay
	* test-requires
	+ JSON:PP, -Test::More
	* test-requires.skip: new

lib/Amazon/S3/Lite.pm  view on Meta::CPAN

  my $options = ref $args[0] ? $args[0] : {@args};

  my $self = bless $options, $class;

  $self->{host}    //= 's3.amazonaws.com';
  $self->{secure}  //= $TRUE;
  $self->{timeout} //= 30;
  $self->{region}  //= 'us-east-1';

  $self->_init_logger;
  $self->_init_credentials;
  $self->_init_ua;

  return $self;
}

########################################################################
# Logger setup
# Priority: caller-supplied object -> Log::Log4perl (if available) ->
#           minimal STDERR logger
########################################################################

lib/Amazon/S3/Lite.pm  view on Meta::CPAN

  }

  # Fall back to minimal STDERR logger
  $self->{logger} = Amazon::S3::Lite::Logger->new( log_level => $log_level );

  return;
}

########################################################################
# Credential resolution
# Priority: explicit credentials object -> constructor args ->
#           environment variables -> Amazon::Credentials (if available)
########################################################################
sub _init_credentials {
########################################################################
  my ($self) = @_;

  # 1. Caller-supplied credentials object (duck-typed)
  if ( my $creds = $self->{credentials} ) {
    croak "credential object is not blessed.\n"
      if !blessed $creds;

    foreach (qw(aws_access_key_id aws_secret_access_key token)) {
      my $sub = $creds->can($_) // $creds->can("get_$_");

      croak "credentials object must implement $_ or get_$_\n"
        if !$sub;
    }

    $self->{credentials} = $creds;

    return;
  }

  # 2. Explicit constructor args
  if ( $self->{aws_access_key_id} && $self->{aws_secret_access_key} ) {
    $self->{credentials} = Amazon::S3::Lite::Credentials->new(
      aws_access_key_id     => delete $self->{aws_access_key_id},
      aws_secret_access_key => delete $self->{aws_secret_access_key},
      token                 => delete $self->{token},
    );
    return;
  }

  # 3. Environment variables
  if ( $ENV{AWS_ACCESS_KEY_ID} && $ENV{AWS_SECRET_ACCESS_KEY} ) {
    $self->{credentials} = Amazon::S3::Lite::Credentials->new(
      aws_access_key_id     => $ENV{AWS_ACCESS_KEY_ID},
      aws_secret_access_key => $ENV{AWS_SECRET_ACCESS_KEY},
      token                 => $ENV{AWS_SESSION_TOKEN},
    );
    return;
  }

  # 4. Amazon::Credentials (covers IAM roles, ECS task roles,
  #    ~/.aws/credentials, etc.)
  if ( eval { require Amazon::Credentials; 1 } ) {
    $self->{credentials} = Amazon::Credentials->new;
    return;
  }

  croak 'No AWS credentials found. Supply aws_access_key_id/'
    . 'aws_secret_access_key, set AWS_ACCESS_KEY_ID/'
    . 'AWS_SECRET_ACCESS_KEY environment variables, '
    . 'or install Amazon::Credentials for IAM role support.';
}

########################################################################
# HTTP::Tiny instance - one per object, keep-alive enabled
########################################################################
sub _init_ua {
########################################################################

lib/Amazon/S3/Lite.pm  view on Meta::CPAN

}

########################################################################
# Accessors
########################################################################
sub logger      { return $_[0]->{logger} }
sub log_level   { return $_[0]->{log_level}; }
sub ua          { return $_[0]->{ua} }
sub region      { return $_[0]->{region} }
sub host        { return $_[0]->{host} }
sub credentials { return $_[0]->{credentials} }

########################################################################
# Build a fresh signer from current credentials.
# Called per-request so that rotating credentials (Lambda IAM roles)
# are always current.
########################################################################
sub _signer {
########################################################################
  my ( $self, $region ) = @_;

  my $creds = $self->credentials;

  my $access_key
    = $creds->can('get_aws_access_key_id')
    ? $creds->get_aws_access_key_id
    : $creds->aws_access_key_id;

  my $secret_key
    = $creds->can('get_aws_secret_access_key')
    ? $creds->get_aws_secret_access_key
    : $creds->aws_secret_access_key;

lib/Amazon/S3/Lite.pm  view on Meta::CPAN

Amazon::S3::Lite - A lightweight Amazon S3 client for common
operations

=head1 SYNOPSIS

  use Amazon::S3::Lite;

  # Credentials from environment or IAM role automatically
  my $s3 = Amazon::S3::Lite->new({ region => 'us-east-1' });

  # Explicit credentials
  my $s3 = Amazon::S3::Lite->new({
    region                => 'us-east-1',
    aws_access_key_id     => $key,
    aws_secret_access_key => $secret,
    token                 => $session_token,  # optional, for STS/Lambda roles
  });

  # Pass any credentials object with standard getters
  my $s3 = Amazon::S3::Lite->new({
    region      => 'us-east-1',
    credentials => $creds_obj,
  });

  # List objects in a bucket
  my $result = $s3->list_objects_v2('my-bucket', prefix => 'logs/');

  foreach my $obj ( @{ $result->{objects} } ) {
    printf "%s  %d bytes\n", $obj->{key}, $obj->{size};
  }

  # Paginate

lib/Amazon/S3/Lite.pm  view on Meta::CPAN

Returns a new C<Amazon::S3::Lite> object. Options:

=over 4

=item region (options, default: us-east-1)

The AWS region for your bucket, e.g. C<us-east-1>.

=item aws_access_key_id / aws_secret_access_key

Static credentials. C<token> may also be supplied for STS temporary
credentials (as used by Lambda execution roles).

These are only consulted if no C<credentials> object is provided.

=item token

Optional STS session token, used alongside static credentials for
temporary credential sets.

=item credentials

An object providing credential getters. The object must respond to:

  $creds->aws_access_key_id
  $creds->aws_secret_access_key
  $creds->token            # may return undef

Any object that satisfies this interface is accepted -
L<Amazon::Credentials>, L<Paws::Credential::*>, or your own. The
getters are called at request time, so objects that refresh expiring
credentials transparently are supported.

=item logger

An object providing the standard log methods:

  $logger->trace(...)
  $logger->debug(...)
  $logger->info(...)
  $logger->warn(...)
  $logger->error(...)

lib/Amazon/S3/Lite.pm  view on Meta::CPAN

local S3-compatible endpoints.

=item timeout

HTTP request timeout in seconds. Default is 30.

=back

=head2 Credential resolution order

When no C<credentials> object is passed, credentials are resolved in
this order:

=over 4

=item 1.

Constructor arguments C<aws_access_key_id> and C<aws_secret_access_key>.

=item 2.

Environment variables C<AWS_ACCESS_KEY_ID>, C<AWS_SECRET_ACCESS_KEY>,
and optionally C<AWS_SESSION_TOKEN>.

=item 3.

L<Amazon::Credentials>, if installed. This covers IAM instance roles,
Lambda execution roles, ECS task roles, and C<~/.aws/credentials>
profiles.

=item 4.

If none of the above yield credentials, the constructor croaks.

=back

=head1 METHODS

All methods croak on unrecoverable errors (network failure, HTTP 5xx).
HTTP 404 is not an exception - methods that can meaningfully return
C<undef> for a missing resource do so.

=head2 list_objects_v2

lib/Amazon/S3/Lite.pm  view on Meta::CPAN


=item * L<Carp> (core)

=back

Optional:

=over 4

=item * L<Amazon::Credentials> - automatic credential discovery from IAM
roles, ECS task roles, ~/.aws/credentials, and environment.

=item * L<Log::Log4perl> - structured logging; if present, used in
preference to the built-in minimal logger.

=back

=head1 LAMBDA USAGE NOTES

In a Lambda container, credentials come from the execution role via
the ECS credential provider endpoint (indicated by
C<AWS_CONTAINER_CREDENTIALS_RELATIVE_URI> in the environment).
L<Amazon::Credentials> handles this automatically when installed and
is the recommended approach. If you prefer not to take that
dependency, the Lambda runtime also populates C<AWS_ACCESS_KEY_ID>,
C<AWS_SECRET_ACCESS_KEY>, and C<AWS_SESSION_TOKEN> directly, which
this module picks up automatically from the environment.

B<Region note:> The C<list_buckets> method is a global S3 operation
and is always signed against C<us-east-1>, regardless of the region

lib/Amazon/S3/Lite/Credentials.pm  view on Meta::CPAN

########################################################################
# Simple immutable credentials object — used when caller passes
# raw key/secret/token rather than a credentials object
########################################################################
package Amazon::S3::Lite::Credentials;

use strict;
use warnings;

use Carp qw(croak);

our $VERSION = '1.2.2';

share/README.md  view on Meta::CPAN

Amazon::S3::Lite - A lightweight Amazon S3 client for common
operations

# SYNOPSIS

    use Amazon::S3::Lite;

    # Credentials from environment or IAM role automatically
    my $s3 = Amazon::S3::Lite->new({ region => 'us-east-1' });

    # Explicit credentials
    my $s3 = Amazon::S3::Lite->new({
      region                => 'us-east-1',
      aws_access_key_id     => $key,
      aws_secret_access_key => $secret,
      token                 => $session_token,  # optional, for STS/Lambda roles
    });

    # Pass any credentials object with standard getters
    my $s3 = Amazon::S3::Lite->new({
      region      => 'us-east-1',
      credentials => $creds_obj,
    });

    # List objects in a bucket
    my $result = $s3->list_objects_v2('my-bucket', prefix => 'logs/');

    foreach my $obj ( @{ $result->{objects} } ) {
      printf "%s  %d bytes\n", $obj->{key}, $obj->{size};
    }

    # Paginate

share/README.md  view on Meta::CPAN

    my $s3 = Amazon::S3::Lite->new(\%options);

Returns a new `Amazon::S3::Lite` object. Options:

- region (options, default: us-east-1)

    The AWS region for your bucket, e.g. `us-east-1`.

- aws\_access\_key\_id / aws\_secret\_access\_key

    Static credentials. `token` may also be supplied for STS temporary
    credentials (as used by Lambda execution roles).

    These are only consulted if no `credentials` object is provided.

- token

    Optional STS session token, used alongside static credentials for
    temporary credential sets.

- credentials

    An object providing credential getters. The object must respond to:

        $creds->aws_access_key_id
        $creds->aws_secret_access_key
        $creds->token            # may return undef

    Any object that satisfies this interface is accepted -
    [Amazon::Credentials](https://metacpan.org/pod/Amazon%3A%3ACredentials), [Paws::Credential::\*](https://metacpan.org/pod/Paws%3A%3ACredential%3A%3A%2A), or your own. The
    getters are called at request time, so objects that refresh expiring
    credentials transparently are supported.

- logger

    An object providing the standard log methods:

        $logger->trace(...)
        $logger->debug(...)
        $logger->info(...)
        $logger->warn(...)
        $logger->error(...)

share/README.md  view on Meta::CPAN


    Use HTTPS. Default is 1 (true). Set to 0 only for testing against
    local S3-compatible endpoints.

- timeout

    HTTP request timeout in seconds. Default is 30.

## Credential resolution order

When no `credentials` object is passed, credentials are resolved in
this order:

1. Constructor arguments `aws_access_key_id` and `aws_secret_access_key`.
2. Environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`,
and optionally `AWS_SESSION_TOKEN`.
3. [Amazon::Credentials](https://metacpan.org/pod/Amazon%3A%3ACredentials), if installed. This covers IAM instance roles,
Lambda execution roles, ECS task roles, and `~/.aws/credentials`
profiles.
4. If none of the above yield credentials, the constructor croaks.

# METHODS

All methods croak on unrecoverable errors (network failure, HTTP 5xx).
HTTP 404 is not an exception - methods that can meaningfully return
`undef` for a missing resource do so.

## list\_objects\_v2

    my $result = $s3->list_objects_v2($bucket, %options);

share/README.md  view on Meta::CPAN

- [Amazon::Signature4::Lite](https://metacpan.org/pod/Amazon%3A%3ASignature4%3A%3ALite)
- [XML::Twig](https://metacpan.org/pod/XML%3A%3ATwig) (for parsing list and copy responses)
- [Digest::MD5](https://metacpan.org/pod/Digest%3A%3AMD5) (core, for Content-MD5 headers)
- [MIME::Base64](https://metacpan.org/pod/MIME%3A%3ABase64) (core)
- [URI::Escape](https://metacpan.org/pod/URI%3A%3AEscape)
- [Carp](https://metacpan.org/pod/Carp) (core)

Optional:

- [Amazon::Credentials](https://metacpan.org/pod/Amazon%3A%3ACredentials) - automatic credential discovery from IAM
roles, ECS task roles, ~/.aws/credentials, and environment.
- [Log::Log4perl](https://metacpan.org/pod/Log%3A%3ALog4perl) - structured logging; if present, used in
preference to the built-in minimal logger.

# LAMBDA USAGE NOTES

In a Lambda container, credentials come from the execution role via
the ECS credential provider endpoint (indicated by
`AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` in the environment).
[Amazon::Credentials](https://metacpan.org/pod/Amazon%3A%3ACredentials) handles this automatically when installed and
is the recommended approach. If you prefer not to take that
dependency, the Lambda runtime also populates `AWS_ACCESS_KEY_ID`,
`AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN` directly, which
this module picks up automatically from the environment.

**Region note:** The `list_buckets` method is a global S3 operation
and is always signed against `us-east-1`, regardless of the region

t/01-s3-lite.t  view on Meta::CPAN

    };
  };
}

########################################################################
# Unit tests — no network required
########################################################################

subtest 'constructor' => sub {

  # no credentials — stub _init_credentials so the test is immune to whether
  # Amazon::Credentials is installed or finds real creds (e.g. on an EC2 instance)
  {
    local $ENV{AWS_ACCESS_KEY_ID}     = undef;
    local $ENV{AWS_SECRET_ACCESS_KEY} = undef;
    no warnings 'redefine';
    local *Amazon::S3::Lite::_init_credentials = sub {
      my ( $self, $args ) = @_;
      Carp::croak 'No AWS credentials found.'
        if !$args->{credentials}
        && !$args->{aws_access_key_id}
        && !$ENV{AWS_ACCESS_KEY_ID};
    };
    eval { Amazon::S3::Lite->new( { region => 'us-east-1' } ) };
    like $@, qr/No AWS credentials/, 'croaks without credentials';
  }

  # explicit credentials
  my $s3 = new_s3();
  isa_ok $s3, 'Amazon::S3::Lite';
  is $s3->region, 'us-east-1',        'region set';
  is $s3->host,   's3.amazonaws.com', 'default host';

  # env credentials
  {
    local $ENV{AWS_ACCESS_KEY_ID}     = 'envkey';
    local $ENV{AWS_SECRET_ACCESS_KEY} = 'envsecret';
    local $ENV{AWS_SESSION_TOKEN}     = 'envtoken';
    my $s3e = Amazon::S3::Lite->new( { region => 'us-east-1' } );
    is $s3e->credentials->aws_access_key_id, 'envkey',   'env key';
    is $s3e->credentials->token,             'envtoken', 'env token';
  }

  # duck-typed credentials object
  {

    package MyCreds;
    sub new                   { bless {}, shift }
    sub aws_access_key_id     {'duckkey'}
    sub aws_secret_access_key {'ducksecret'}
    sub token                 {undef}

    package main;
    my $s3d = Amazon::S3::Lite->new(
      { region      => 'us-east-1',
        credentials => MyCreds->new,
      }
    );
    is $s3d->credentials->aws_access_key_id, 'duckkey', 'duck-type creds';
  }

  # bad credentials object
  {

    package BadCreds;
    sub new               { bless {}, shift }
    sub aws_access_key_id {'key'}

    package main;
    eval { Amazon::S3::Lite->new( { region => 'us-east-1', credentials => BadCreds->new } ) };
    like $@, qr/must implement aws_secret_access_key/, 'bad creds object croaks';
  }

  # custom logger
  {
    my $warned = 0;
    my $logger = bless {}, 'MyLogger';
    {
      no strict 'refs';
      for my $m (qw(trace debug info error)) {



( run in 0.690 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )