module Eco module API module Common module Session class S3Uploader include Eco::Language::AuxiliarLogger attr_reader :prefix # @param enviro [Eco::API::Common::Session::Environment] def initialize(enviro:) msg = "Required Environment object (enviro:). Given: #{enviro.class}" raise msg if enviro && !enviro.is_a?(Eco::API::Common::Session::Environment) @enviro = enviro @prefix = fetch_prefix @timestamp = Time.now.iso8601 end # Uploads `content` to S3 as `filename` # @param filename [String] the name of the object to be created on S3 # @param content [String] that to be uploaded # @return [String] S3 path to the uploaded `filename` object def upload(filename, content) if (obj = new_s3_object(filename)) log_upload(obj) do obj.put(body: content) end end full_path(obj) end # Uploads a single file # @param path [String] the target file to be uploaded # @return [String] S3 path to the uploaded `path` file def upload_file(path) return unless File.exist?(path) File.open(path, "rb") do |f| upload(File.basename(path), f) end end # @note it will skip subfolders # @param path [String] the target directory to be uploaded # @param recurse [Boolean] deepen in the folder structure? (`false`: default) # @return [Array] S3 paths to all the uploaded files of `path` directory def upload_directory(path, recurse: false) path = File.expand_path(path) prefix = File.expand_path(File.join(path, "..")) wildcard = recurse ? "**/*" : "*" Dir.glob(File.join(path, wildcard)).sort.map do |file| next unless File.file?(file) # Skip directories key = file.sub(prefix, "").gsub(/\\/, "/").sub(/^\/+/, "") File.open(file, "rb") do |f| upload(key, f) end end.compact end # @param path [String] a full path to a S3 object # @return [String] `link` to the S3 object on console def link(path) return path.map { |pth| link(pth) } if path.is_a?(Enumerable) return unless path.is_a?(String) s3_path_str = "https://s3.console.aws.amazon.com/s3/object" s3_params = "region=#{fetch_region}&tab=overview" "#{s3_path_str}/#{path.sub('s3://', '')}?#{s3_params}" end private def full_path(obj) "s3://#{bucket.name}/#{obj.key}" if obj end def bucket require 'aws-sdk-s3' begin @bucket ||= Aws::S3::Resource.new( access_key_id: fetch_access_key_id, secret_access_key: fetch_secret_access_key, region: fetch_region ).bucket(fetch_bucket) rescue StandardError => err log(:error) { "Trying to upload to S3 with wrong configuration: #{err}" } end @bucket end def session_path ["scripts", @prefix, @timestamp].join("/") end def new_s3_object(filename) file_path = [session_path, filename].join("/") bucket&.object(file_path) end def log_upload(obj) yield log(:debug) { "Uploaded #{full_path(obj)}" } end def logger @enviro&.logger || super end def config @enviro.config || {} end def fetch_bucket config.s3storage.bucket_name end def fetch_prefix config.s3storage.prefix end def fetch_access_key_id config.s3storage.access_key_id || ENV['AWS_ACCESS_KEY_ID'] end def fetch_secret_access_key config.s3storage.secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'] end def fetch_region config.s3storage.region || ENV['AWS_REGION'] end end end end end end