class BFS::Bucket::S3

S3 buckets are operating on s3

Attributes

acl[R]
name[R]
sse[R]
storage_class[R]

Public Class Methods

new(name, **opts) click to toggle source

Initializes a new S3 bucket @param [String] name the bucket name @param [Hash] opts options @option opts [String] :region default region @option opts [String] :prefix custom namespace within the bucket @option opts [String] :sse default server-side-encryption setting @option opts [Aws::Credentials] :credentials credentials object @option opts [String] :access_key_id custom AWS access key ID @option opts [String] :secret_access_key custom AWS secret access key @option opts [String] :profile_name custom AWS profile name (for shared credentials) @option opts [Symbol] :acl canned ACL @option opts [String] :storage_class storage class @option opts [Aws::S3::Client] :client custom client, uses default_client by default

Calls superclass method
# File lib/bfs/bucket/s3.rb, line 23
def initialize(name, **opts)
  super(**opts)

  @name = name
  @sse = opts[:sse] || opts[:server_side_encryption]
  @prefix = opts[:prefix]
  @acl = opts[:acl].to_sym if opts[:acl]
  @storage_class = opts[:storage_class]
  @client = opts[:client] || init_client(**opts)
end

Public Instance Methods

cp(src, dst, **opts) click to toggle source

Copies a file.

# File lib/bfs/bucket/s3.rb, line 123
def cp(src, dst, **opts)
  src = full_path(src)
  dst = full_path(dst)
  opts = opts.merge(
    bucket: name,
    copy_source: "/#{name}/#{src}",
    key: dst,
  )
  @client.copy_object(**opts)
rescue Aws::S3::Errors::NoSuchKey, Aws::S3::Errors::NoSuchBucket, Aws::S3::Errors::NotFound
  raise BFS::FileNotFound, trim_prefix(src)
end
create(path, encoding: self.encoding, perm: self.perm, **opts, &block) click to toggle source

Creates a new file and opens it for writing @param [String] path @param [Hash] opts options @option opts [String] :encoding Custom encoding. @option opts [String] :acl custom ACL override @option opts [String] :server_side_encryption SSE override @option opts [String] :storage_class storage class override

# File lib/bfs/bucket/s3.rb, line 72
def create(path, encoding: self.encoding, perm: self.perm, **opts, &block)
  path = full_path(path)
  opts = opts.merge(
    bucket: name,
    key: path,
  )
  opts[:acl] ||= @acl if @acl
  opts[:server_side_encryption] ||= @sse if @sse
  opts[:storage_class] ||= @storage_class if @storage_class

  BFS::Writer.new(path, encoding: encoding, perm: perm) do |t|
    File.open(t, encoding: encoding) do |file|
      @client.put_object(opts.merge(body: file))
    end
  end.perform(&block)
end
glob(pattern = '**/*', **opts) click to toggle source

Iterates over the contents of a bucket using a glob pattern

# File lib/bfs/bucket/s3.rb, line 44
def glob(pattern = '**/*', **opts)
  Enumerator.new do |acc|
    walk(pattern, **opts) do |path, obj|
      info = BFS::FileInfo.new(path: path, size: obj.size, mtime: obj.last_modified)
      acc << info
    end
  end
end
info(path, **opts) click to toggle source

Info returns the object info

# File lib/bfs/bucket/s3.rb, line 54
def info(path, **opts)
  path = norm_path(path)
  opts = opts.merge(bucket: name, key: full_path(path))
  info = @client.head_object(**opts)
  raise BFS::FileNotFound, path unless info

  BFS::FileInfo.new path: path, size: info.content_length, mtime: info.last_modified, content_type: info.content_type, metadata: norm_meta(info.metadata)
rescue Aws::S3::Errors::NoSuchKey, Aws::S3::Errors::NoSuchBucket, Aws::S3::Errors::NotFound
  raise BFS::FileNotFound, path
end
ls(pattern = '**/*', **opts) click to toggle source

Lists the contents of a bucket using a glob pattern

# File lib/bfs/bucket/s3.rb, line 35
def ls(pattern = '**/*', **opts)
  Enumerator.new do |acc|
    walk(pattern, **opts) do |path, _|
      acc << path
    end
  end
end
open(path, encoding: self.encoding, tempdir: nil, **opts, &block) click to toggle source

Opens an existing file for reading @param [String] path @param [Hash] opts options @option opts [String] :encoding Custom encoding. @option opts [String] :tempdir Custom temp dir.

# File lib/bfs/bucket/s3.rb, line 94
def open(path, encoding: self.encoding, tempdir: nil, **opts, &block)
  path = full_path(path)
  temp = Tempfile.new(File.basename(path), tempdir, encoding: encoding)
  temp.close

  opts = opts.merge(
    response_target: temp.path,
    bucket: name,
    key: path,
  )
  @client.get_object(**opts)

  File.open(temp.path, encoding: encoding, &block)
rescue Aws::S3::Errors::NoSuchKey, Aws::S3::Errors::NoSuchBucket, Aws::S3::Errors::NotFound
  raise BFS::FileNotFound, trim_prefix(path)
end
rm(path, **opts) click to toggle source

Deletes a file.

# File lib/bfs/bucket/s3.rb, line 112
def rm(path, **opts)
  path = full_path(path)
  opts = opts.merge(
    bucket: name,
    key: path,
  )
  @client.delete_object(**opts)
rescue Aws::S3::Errors::NoSuchKey, Aws::S3::Errors::NoSuchBucket, Aws::S3::Errors::NotFound # rubocop:disable Lint/SuppressedException
end

Private Instance Methods

assume_role_credentials(role_arn, credentials = nil) click to toggle source
# File lib/bfs/bucket/s3.rb, line 149
def assume_role_credentials(role_arn, credentials = nil)
  opts = {
    role_arn: role_arn,
    role_session_name: SecureRandom.urlsafe_base64(12),
  }
  opts[:client] = Aws::STS::Client.new(credentials: credentials) if credentials
  Aws::AssumeRoleCredentials.new(**opts)
end
init_client(**opts) click to toggle source
# File lib/bfs/bucket/s3.rb, line 138
def init_client(**opts)
  config = {}
  config[:region] = opts[:region] if opts[:region]
  config[:credentials] = opts[:credentials] if opts[:credentials]
  config[:credentials] ||= Aws::Credentials.new(opts[:access_key_id].to_s, opts[:secret_access_key].to_s) if opts[:access_key_id]
  config[:credentials] ||= Aws::SharedCredentials.new(profile_name: opts[:profile_name]) if opts[:profile_name]
  config[:credentials] = assume_role_credentials(opts[:assume_role], config[:credentials]) if opts[:assume_role]

  Aws::S3::Client.new(config)
end
walk(pattern, **opts) { |path, obj| ... } click to toggle source
# File lib/bfs/bucket/s3.rb, line 158
def walk(pattern, **opts)
  prefix = pattern[%r{^[^*?\{\}\[\]]+/}]
  prefix = File.join(*[@prefix, prefix].compact) if @prefix

  opts = opts.merge(bucket: name, prefix: @prefix)
  opts[:prefix] = prefix if prefix

  next_token = nil
  loop do
    resp = @client.list_objects_v2 opts.merge(continuation_token: next_token)
    resp.contents.each do |obj|
      path = trim_prefix(obj.key)
      yield(path, obj) if File.fnmatch?(pattern, path, File::FNM_PATHNAME)
    end
    next_token = resp.next_continuation_token.to_s
    break if next_token.empty?
  end
end