Use knife commands from ruby without bash - ruby

I'm trying to remove the following bash command from my ruby script:
nodes = "knife search 'chef_environment:#{env} AND recipe:#{microservice}' -i 2>&1 | tail -n 2"
node = %x[ #{nodes} ].split
node.each do |n|
puts n
end
And replace it with something like this:
node = Chef::Knife.search("chef_environment:#{env} AND recipe:#{microservice}").split
Is this possible? Is there any documentation regarding Chef::knife library in ruby and how to use it?

To access a chef server, you could try to use the ridley gem, which is also used by Berkshelf and thus generally up-to-date.
A usage example could be:
ridley = Ridley.from_chef_config('/path/to/knife.rb')
ridley.search(:node, "chef_environment:#{env} AND recipe:#{microservice}")
See the documentation of the gem for a more detailed description of its options.

Related

How can I determine what the current stable version of Ruby is?

I want to write a Ruby method that does two things:
Determine what the current stable version of Ruby is. My first thought is to get the response from https://www.ruby-lang.org/en/downloads/ and use RegEx to isolate the phrase The current stable version is [x]. Is there is an API I'm not aware of?
Get the URL to download the .tar.gz of that release. For this I was thinking the same thing, get it from the output of the site URL.
I'm looking for advice about the best way to go about it, or direction if there's something in place I might use to determine my desired results.
Ruby code to fetch the download page, then parse the current version and the link URL:
html = Net::HTTP.get(URI("https://www.ruby-lang.org/en/downloads/"))
vers = html[/http.*ruby-(.*).tar.gz/,1]
link = html[/http.*ruby-.*.tar.gz/]
GitHub code: ruby-stable-version.rb
Shell code:
ruby-stable-version
If you are using rbenv you can use ruby-build to get a list of ruby versions and then grep against that.
ruby-build --definitions | tail -r | grep -x -G -m 1 '[0-9]\.[0-9].[0-9]\-*[p0-9*]*'
You can then use that within your code like so:
version = `ruby-build --definitions | tail -r | grep -x -G -m 1 '[0-9]\.[0-9].[0-9]\-*[p0-9*]*'`.strip
You can then use this value to get the download URL.
url = "http://cache.ruby-lang.org/pub/ruby/#{version[0..2]}/ruby-#{version}.tar.gz"
And then download the file:
require 'open-uri'
open("ruby-#{version}.tar.gz", 'wb') do |file|
file << open(url).read
end
Learn more about rbenv here and ruby-build here.
Another possibility would be to use the Ruby source repository. Check version.h in every branch, filter by RUBY_PATCHLEVEL > -1 (-1 is used for -dev versions), sort by RUBY_VERSION and take the latest one.
You can use:
Ruby's built-in OpenURI, and Nokogiri, to read a page, parse it, search for certain tags, extract a parameter such as a "src" or "href".
OpenURI to read the URL, or curl or wget at the command-line to retrieve the file.
Nokogiri's tutorials including showing how to use OpenURI to retrieve the page and hand it off to Nokogiri.
OpenURI's docs show how to "open" URLs and retrieve their content using read. Once you've done that, the data will be easy to save to disk using something like this for text files:
File.write('some_file', open('http://www.example.com/').read)
or for binary:
File.open('some_file', 'wb') { |fo| fo.write(open('http://www.example.com/').read) }
There are examples of using both Nokogiri and OpenURI for this all over Stack Overflow.

Getting recipe list through chef server api

I want to get the recipes that a cookbook contains, through chef-server-api. Following is the code I'm using for getting the cookbook list, individual cookbook details through the api :
require 'rubygems'
require 'chef/config'
require 'chef/log'
require 'chef/rest'
require 'chef/cookbook_version'
client_name = "admin"
signing_key_filename="c:/chef-repo/.chef/admin.pem"
server_url = "https://10.132.17.244:443"
rest = Chef::REST.new(server_url, client_name, signing_key_filename)
cookbooks = rest.get_rest("/cookbooks?all_versions")
cookbooks.keys.each do |name|
cookbook_versions = rest.get_rest("/cookbooks/#{name}")
print "#{name}\n"
cookbook_versions[name]["versions"].each do |cv|
version = cv["version"]
cookbook = rest.get_rest("/cookbooks/#{name}/#{version}")
print "\t#{cookbook}\n"
#parsed = JSON[cookbook]
end
end
The problem I'm facing is to get the recipe list from the 'cookbook' object. I tried parsing it to ruby hash and then read, but of no use. If I directly print the 'cookbook' variable, the output is something like the screenshot
I'm not able to get how to interpret the output I am getting by hitting the '/cookbooks/NAMEW/VERSION' endpoint, and get the recipes present in an individual cookbooks.
When using the Chef gem it automatically decodes some responses into Ruby objects for you. You can either use the object directly (specifically you want #recipe_filenames and then parse those to the cookbook_name::recipe_name format) or you could use a better API client like Chef-API or PyChef.
Need a ruby solution? The following example uses jq to filter the JSON resultset returned by knife:
$ knife cookbook show apache2 2.0.0 recipes -Fj | jq '.[]|.name'
"mod_cgi.rb"
"mod_proxy_http.rb"
"mod_proxy_html.rb"
"mod_access_compat.rb"
"mod_authz_dbd.rb"
"mod_proxy_express.rb"
..
..

Find latest available RVM version number

I can't seem to find an easy way to identify the latest release of RVM from command line or rvm.beginrescueend.com!?!
I currently type rvm get latest every few days or so to update RVM. If version is same, RVM goes through the download & update process regardless. I'd like to be able to first 'see' if there's an update to get.
Anyone know how? I'm sure I'm missing the obvious...
you could use this one liner to check version:
$ curl -sS https://api.github.com/repos/wayneeseguin/rvm/git/refs/tags | awk -F": |\"" '$2=="ref"{sub(/.*\//,"",$5); print $5}' | sort -V | tail -n 1
1.15.8
or a pure ruby one liner:
$ ruby -ropen-uri -rjson -e 'open("https://api.github.com/repos/wayneeseguin/rvm/git/refs/tags"){|r| puts JSON.parse(r.read).map{|l| l["ref"].gsub(/.*\//,"").split(".").map(&:to_i)}.sort.last.join(".") }'
1.15.8
but the simplest thing to do is:
$ curl https://raw.github.com/wayneeseguin/rvm/stable/VERSION
1.15.8
Curl the rvm repository like this:
curl https://raw.githubusercontent.com/rvm/rvm/master/VERSION
Ok, magic :)
Place this into some .rb file :)
require 'open-uri'
require 'openssl'
regex = Regexp.new(/data-name="([0-9]+).([0-9]+).([0-9]+)"/)
f=open("https://github.com/wayneeseguin/rvm",:ssl_verify_mode => OpenSSL::SSL::VERIFY_NONE)
v = []
f.each_line do |l|
regex.match(l) {|m| v << {:full => m[0], :major => m[1].to_i, :minor => m[2].to_i, :inc => m[3].to_i} }
end
v.sort_by{|m| [m[:major],m[:minor],m[:inc]] }
v=v.first
puts "#{v[:major]}.#{v[:minor]}.#{v[:inc]}"
I have no idea why I just did that.
I used:
$ rvm get head && rvm reload
It ran fast and seemed to do the job. I had installed rvm the week before, rvm 1.15.5, and already there was a newer version, rvm 1.15.8.
This was recommended on:
The Ruby on Rails Tutorial by Michael Hartl
http://ruby.railstutorial.org/ruby-on-rails-tutorial-book#sec-install_ruby

Is it possible to run a single test in MiniTest?

I can run all tests in a single file with:
rake test TEST=path/to/test_file.rb
However, if I want to run just one test in that file, how would I do it?
I'm looking for similar functionality to:
rspec path/to/test_file.rb -l 25
The command should be:
% rake test TEST=test/test_foobar.rb TESTOPTS="--name=test_foobar1 -v"
Have you tried:
ruby path/to/test_file.rb --name test_method_name
No gem required:
ruby -Itest test/lib/test.rb --name /some_test/
Source: http://blog.arvidandersson.se/2012/03/28/minimalicous-testing-in-ruby-1-9
This is one of the things that bother me about the string name definition in tests.
When you have:
def test_my_test
end
you always know how your test is named so you can execute it like this:
ruby my_test -n test_my_test
But when you have something like:
it "my test" do
end
you are never sure how this test is really named internally so you can not use the -n option just directly.
To know how this test is named internally you only have an option: execute the whole file to try to figure out looking in the logs.
My workaround is (temporally) add something to the test name very unique like:
it "my test xxx" do
end
and then use the RegEx version of the '-n' parameter like:
ruby my_test.rb -n /xxx/
I'm looking for similar functionality to rspec path/to/file.rb -l 25
With Nick Quaranto's "m" gem, you can say:
m spec/my_spec.rb:25
If you are using MiniTest with Rails 5+ the best way to run all tests in a single file is:
bin/rails test path/to/test_file.rb
And for a single test (e.g. on line 25):
bin/rails test path/to/test_file.rb:25
See http://guides.rubyonrails.org/testing.html#the-rails-test-runner
You can use this to run a single file:
rake test TEST=test/path/to/file.rb
I also used
ruby -I"lib:test" test/path/to/file.rb
for better display.
There are 2 ways to do it:
Run tests 'manually' (see Andrew Grimm's answer).
Hack Rake::TestTask target to use a different tests loader.
Rake::TestTask (from rake 0.8.7) theoretically is able to pass additional options to MiniTest::Unit with a "TESTOPTS=blah-blah" command line option, for example:
% rake test TEST=test/test_foobar.rb TESTOPTS="--name test_foobar1 -v"
In practice, the option --name (a filter for test names) won't work, due to rake internals. To fix that you'll need to write a small monkey patch in your Rakefile:
# overriding the default rake tests loader
class Rake::TestTask
def rake_loader
'test/my-minitest-loader.rb'
end
end
# our usual test terget
Rake::TestTask.new {|i|
i.test_files = FileList['test/test_*.rb']
i.verbose = true
}
This patch requires you to create a file test/my-minitest-loader.rb:
ARGV.each { |f|
break if f =~ /^-/
load f
}
To print all possible options for Minitest, type
% ruby -r minitest/autorun -e '' -- --help
You can pass --name to run a test by its name or a number within its name:
-n, --name PATTERN Filter run on /regexp/ or string.
e.g.:
$ ruby spec/stories/foo_spec.rb --name 3
FAIL (0:00:00.022) test_0003_has foo
Expected: "foo"
Actual: nil
This flag is documented in Minitest’s README.
I am in Rails Version 4.2.11.3 and Ruby Version 2.4.7p357
Below one worked for me.
ruby -Itest <relative_minitest_file_path> --name /<test_name>/
If you are using Turn gem with minitest, just make sure to use Turn.config.pattern option since Turn Minitest runner doesn't respect --name option in ARGs.
I'm looking for similar functionality to:
rspec path/to/test_file.rb -l 25
There is a gem that does exactly that: minitest-line.
gem install minitest-line
ruby test/my_file -l 5
from https://github.com/judofyr/minitest-line#minitest-line
I use ruby /path/to/test -n /distinguishable word/
Edit:
-n is a shorthand for --name. distinguishable word can be any string you put in the test description, I usually use some random word that I know won't be present in other tests' descriptions.
Following will work
def test_abc
end
test "hello world"
end
This can run by
bundle exec ruby -I test path/to/test -n test_abc
bundle exec ruby -I test path/to/test -n test_hello_word
Install gem minitest-focus and use the keyword focus on test/spec like below to run only the specific test.
focus
def test
end
focus
it "test" do
end
This would not need any command line argument to be passed.

Accessing info about rubygem from within ruby script

I need to get the installation path for a given ruby gem and can't find any information on how to do that. Given:
#!/usr/bin/env ruby
require 'rubygems'
require 'somegem'
How can I find out where the installation path of somegem is on the system (without resorting to system(gem ...). The gem in question comes with some icons which I want to reference in my script.
Thanks to Chris I now have the following assembled:
require 'rubygems/Commands/contents_command'
c = Gem::Commands::ContentsCommand.new
c.options[:args] = 'somegem'
c.execute
However, c.execute immediately outputs the result on stdout. How can I catch that in a variable for further processing? res = c.execute does not work.
You have different ways to achieve this:
Gem.source_index.find_name('somegem').last.full_gem_path
You could also just grep your load path:
$:.grep /somegem/

Resources