We have the requirement to compress and send files to S3 with zstd compression. I am trying to implement zstandard compression plugin with fluentd s3 plugin so that the data that is sent to s3 using fluentd agent is stored in zstd format and not the default gzip. Not able to find any solution yet. Any help is appreciated.
Installed td-agent fluentd in a Linux system and installed zstd-ruby and ztsd gems. Created the s3_compressor_zstd.rb algo and copied it to /opt/x.x.x.x/fluent-plugin-s3-1.7.2/lib/fluent/plugin directory. While starting the fluentd agent I am getting the below error:
OS: Ubuntu 18.04.6 LTS
td-agent version: 4.4.2
fluentd version: 1.15.3
zstd version: 1.1.2.1
zstd-ruby version: 1.5.2.3
s3_compressor_zstd.rb file:
require "zstd"
module Fluent::Plugin
class S3Output
class ZstdCompressor < Compressor
S3Output.register_compressor('zstd', self)
def initialize(options = {})
begin
require "zstd"
rescue LoadError
raise Fluent::ConfigError, "Install zstd-ruby gem before using zstd compressor"
end
end
def ext
".zst"
end
def compress(data)
compressed_data = ''
Zstd::Writer.open(StringIO.new(compressed_data)) do |compressed_stream|
compressed_stream.write(data)
end
compressed_data
end
end
end
end
According to Use your (de)compression algorithm, compress takes two arguments:
# chunk is buffer chunk. tmp is destination file for upload
def compress(chunk, tmp)
# call command or something
end
You need to update your implementation accordingly.
Check the implementation of the existing supported compressors here:
https://github.com/fluent/fluent-plugin-s3/tree/master/lib/fluent/plugin
Here's a thread that discusses snappy compressor (might be helpful):
https://groups.google.com/g/fluentd/c/4KME4WB-np8
UPDATE
Using zstd gem, the implementation of the compressor may look like this (not tested):
require 'zstd'
module Fluent::Plugin
class S3Output
class LZMA2Compressor < Compressor
S3Output.register_compressor('zstd', self)
def initialize(options = {})
begin
require 'zstd'
rescue LoadError
raise Fluent::ConfigError, 'zstd gem not installed, run: gem install zstd'
end
end
def ext
'zst'.freeze
end
def compress(chunk, tmp)
s = StringIO.new
chunk.write_to(s)
tmp.write(Zstd.new.compress(s.string))
end
end
end
end
Related
Cannot find type `Sinatra::Base`
ruby file
class StaticApp < Sinatra::Base
end
rbs file
class StaticApp < Sinatra::Base
end
run
bundle exec steep check --log-level=fatal
result
[error] Cannot find type `Sinatra::Base`
Diagnostic ID: RBS::UnknownTypeName
I use steep gem. It seems it is needed to require some files. But
library 'sinatra'
doesn't work.
#<RBS::EnvironmentLoader::UnknownLibraryError: Cannot find type definitions for library: sinatra ([nil])>
What do I wrong?
Thanks.
But library 'sinatra' doesn't work ..
RBS::EnvironmentLoader::UnknownLibraryError
Short answer
# Steepfile
target :app do
repo_path 'vendor/rbs'
Create a directory structure like vendor/rbs/sinatra/0/sinatra.rbs.
How you write or generate the contents of sinatra.rbs is out of the scope of this answer, but check out:
rbs collection
gem_rbs_collection
rbs_rails
Long answer
Steep is using an RBS::EnvironmentLoader.
# steep-0.47.0/lib/steep/project/target.rb:54
loader = RBS::EnvironmentLoader.new(core_root: core_root_path, repository: repo)
options.libraries.each do |lib|
name, version = lib.split(/:/, 2)
loader.add(library: name, version: version)
end
The EnvironmentLoader can find a library in either gem_sig_path (I think this would be a sig folder distributed in the gem), or in a repository.
# rbs-1.7.1/lib/rbs/environment_loader.rb:68
def has_library?(library:, version:)
if self.class.gem_sig_path(library, version) || repository.lookup(library, version)
true
else
false
end
end
In a Steepfile, repositories are configured via repo_path.
# Steepfile
repo_path 'vendor/rbs'
For the directory structure of a repository, I took https://github.com/ruby/rbs/tree/master/stdlib as an example.
ls -R vendor/rbs
activesupport
vendor/rbs/activesupport:
0
vendor/rbs/activesupport/0:
activesupport.rbs
I'm creating a mix escript, and all seems well, but I can't invoke the generated escript executable.
>mix escript.build
lib/cmdlineutil.ex:2: warning: variable args is unused
Compiled lib/cmdlineutil.ex
Generated cmdlineutil.app
Consolidated Access
Consolidated Collectable
Consolidated Enumerable
Consolidated Inspect
Consolidated List.Chars
Consolidated Range.Iterator
Consolidated String.Chars
Consolidated protocols written to _build/dev/consolidated
Generated escript cmdlineutil with MIX_ENV=dev
>cmdlineutil
'cmdlineutil' is not recognized as an internal or external command,
operable program or batch file.
>mv cmdlineutil cmdlineutil.exe
>cmdlineutil.exe
This version of C:\Git\elixir\cmdlineutil\cmdlineutil.exe is not
compatible with the version of Windows that you're running. Check your
computer's system information and then contact the software publisher.
Message box:
Unsupported 16-Bit Application
The program or feature "\??\C:\Git\elixir\cmdlineutil\cmdlineutil.exe"
cannot start or run due to incompatibility with 64-bit versions of
Windows. Please contact the software vendor to ask if a 64-bit
Windows compatible version is available.
lib\cmdlineutil.ex:
defmodule CmdLineUtil.Echo do
def main(args) do
IO.puts "Hello!"
end
end
mix.exs:
defmodule CmdLineUtil.Echo.Mixfile do
use Mix.Project
def project do
[app: :cmdlineutil,
version: "0.0.1",
elixir: "~> 1.0.0",
escript: escript,
deps: deps]
end
def escript do
[main_module: CmdLineUtil.Echo]
end
# Configuration for the OTP application
#
# Type `mix help compile.app` for more information
def application do
[applications: [:logger]]
end
# Dependencies can be Hex packages:
#
# {:mydep, "~> 0.3.0"}
#
# Or git/path repositories:
#
# {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"}
#
# Type `mix help deps` for more examples and options
defp deps do
[]
end
end
Theres an escript command installed with Erlang.
>escript cmdlineutil
I am a newbie in ruby and trying to get my hands dirty in chef. I have written a wrapper cookbook on postgresql community cookbook and wish to test it using test kitchen. Following is the spec.rb file I have written:
require 'serverspec'
require 'pg'
include Serverspec::Helper::Exec
include Serverspec::Helper::DetectOS
RSpec.configure do |c|
c.before :all do
c.path = '/sbin:/usr/sbin'
c.os = backend(Serverspec::Commands::Base).check_os
end
end
describe "Postgresql server" do
it "should connect to database" do
conn = PG::Connection.open(:dbname => "db",:user => "user1",:password => "password")
conn.status == "CONNECTION_OK"
end
end
Through this test I wish to check if the user and database have been created properly.
However this test is unable to resolve the dependency of "pg". Where do I mention this dependency in serverspec?
I have used kitchen verify [node name] to run the test.
Create the Ruby code necessary to install the gem prior to requiring it in your spec_helper.rb file (or on the top of the spec file if it makes more sense):
begin
Gem::Specification.find_by_name('pg')
rescue Gem::LoadError
require 'rubygems/dependency_installer'
Gem::DependencyInstaller.new(Gem::DependencyInstaller::DEFAULT_OPTIONS).install('pg')
end
require 'pg'
Here My Problem: Warbler doesn't support gems which are included via path in the gemfile. nevertheless i need to do that for my work. it is important that the included gem is packed and handled as a simple rubygem in the war archive. until now i have tried to manipulate the bundler so when the spec arrives in the warbler/traits/bundler.rb (that's where the specs are packed to the archive) it already has 'Bundler::Source::Rubygems' as source. the problem is that it sill needs to be build and installed from the path, but i can't handle it to pass the path anywhere in the spec or in the source. it already works that the gem is built, installed and packed into the archive as a rubygem and listed under GEM in the Lockfile but only with bad coding (it is all with reference to a specific gem and the path is typed in clearly)
Here my code:
warbler/lib/warbler/traits/bunlder.rb line: 60
case spec.source
when ::Bundler::Source::Git
config.bundler[:git_specs] ||= []
config.bundler[:git_specs] << spec
when ::Bundler::Source::Path
$stderr.puts("warning: Bundler `path' components are not currently supported.",
"The `#{spec.full_name}' component was not bundled.",
"Your application may fail to boot!")
else
##################################################################### MINE
if spec.name == "charla_common"
path = ::Bundler::GemHelper.new("../../common/trunk", spec.name).install_gem
end
##################################################################### MINE END
config.gems << spec
end
This is where the gem is installed path
Bundler/lib/bundler/dsl.rb line: 120
def source(source, options = {})
############################################################### MINE
if source.class == Bundler::Source::Path
options[:path] = source.options["path"]
source = "https://rubygems.org"
end
############################################################### MINE END
case source
when :gemcutter, :rubygems, :rubyforge then
Bundler.ui.warn "The source :#{source} is deprecated because HTTP " \
"requests are insecure.\nPlease change your source to 'https://" \
"rubygems.org' if possible, or 'http://rubygems.org' if not."
#rubygems_source.add_remote "http://rubygems.org"
return
when String
# ensures that the source in the lockfile is shown only once
unless options[:prepend]
#rubygems_source.add_remiote source
end
return
else
#source = source
if options[:prepend]
#sources = [#source] | #sources
else
#sources = #sources | [#source]
end
yield if block_given?
return #source
end
ensure
#source = nil
end
I'm not sure if this is an option for you, but in order to work around this problem, I create symbolic links in vendor/cache to the path with the included gem.
E.g. vendor/cache/gem_name -> ../../../gem_name.
Is there anyway to get Ruby's require statement to download a file from somewhere like github rather than just the local file system?
Update: Sorry I should have made the question clearer. I want to download a file that contains Ruby module and import it into my script rather than just downloading an image or some other arbitrary file within my script.
In other words something like this
require 'http:\\github.com\myrepo\snippet.rb'
puts 'hi'
By default, this is not possible. Also, it's not a good idea for security reasons.
You have a couple of alternatives. If the file you want to include is a Gem and Git repository, then you can use Bundler to download and package the dependency in your project. Then you'll be able to require the file directly in your source code.
This is the best and safest way to include an external dependency.
If you trust the source and you really know what you are doing, you can download the file using Net::HTTP (or any other HTTP library) and eval the body directly in your Ruby code.
You can package everything in a custom require_remote function.
You could download and eval it
require "open-uri"
alias :require_old :require
def require(path)
return false if $".include?(path)
unless path=~ /\Ahttp:\/\/
return require_old(path)
end
eval(open(path).read)
$"<< path
true
end
Be aware, this code has no error checking for network outages nonexisting files, ... . I also believe it is in general not a good idea to require libraries this way, there are security and reliability problems in this approach. But maybe you have a valid usecase for this.
you can include a remote gem from within Gemfiles then it will download when you run bundle install
After reading this question and answers I wanted something a little more bullet proof and verbose that used a paradigm of creating a local file from a repo and then requiring it, only if it didn't already exist locally already. The request for the repo version is explicit via the method repo_require. Used on files you control, this approach improves security IMO.
# try local load
def local_require(filename, relative_path)
relative_flname = File.join(relative_path, filename)
require_relative(relative_flname)
end
# try loading locally first, try repo version on load error
# caution: only use with files you control access to!
def repo_require(raw_repo_prefix, filename, relative_path = '')
local_require(filename, relative_path)
rescue LoadError => e
puts e.message
require 'open-uri'
tempdir = Dir.mktmpdir("repo_require-")
temp_flname = File.join(tempdir, File.basename(filename))
return false if $LOADED_FEATURES.include?(temp_flname)
remote_flname = File.join(raw_repo_prefix, filename)
puts "file not found locally, checking repo: #{remote_flname}"
begin
File.open(temp_flname, 'w') do |f|
f.write(URI.parse(remote_flname).read)
end
rescue OpenURI::HTTPError => e
raise "Error: Can't load #{filename} from repo: #{e.message} - #{remote_flname}"
end
require(temp_flname)
FileUtils.remove_entry(tempdir)
end
Then you could call repo_require like this:
repo_require('https://raw.githubusercontent.com/username/reponame/branch',
'filename', 'relative_path')
The relative_path would the the relative path you would use for the file if the repo was locally installed. For example, you may have something like require_relative '../lib/utils.rb'. In this example filename='lib/utils.rb' and relative_path='..'. This information allows the repo url to be constructed correctly as it does not use the relative path portion.