How to override dependency version, when writing wrapper cookbooks, in chef - ruby

eg.
I am writing a wrapper cookbook (my_cookbook) for the same purpose I am using the community cookbook (community_cookbook). I face an issue regarding the dependency list present in community_cookbook.
The metadata.rb file in community_cookbook has the following dependency list.
...
depends mysql
depends postfix
...
So, by default the run-list has the latest version (say 8.3.0) of mysql cookbook.
My question is what changes should I be making in order to get previous version of mysql (say 5.3.6).
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
What steps did I take to solve this problem ?
Ans:
I wrote the following in metadata.rb of my_cookbook.
...
depends 'mysql', '~> 5.6.3'
depends 'community_cookbook', '~> 1.1.0'
...
And I got this following message on the console
>>>>>> Failed to complete #converge action: [Unable to satisfy the following requirements:
- `mysql (= 8.3.0)` required by `user-specified dependency`
- `mysql (~> 5.6.3)` required by `my_cookbook-0.1.0`
- `mysql (>= 0.0.0)` required by `community_cookbook-1.1.0`
- `mysql (>= 6.0.0)` required by `php-3.0.0` # This is because the community_cookbook also has php as it's dependency list.

You might have changed the Berksfile.lock manually and it is causing the above issue.
can you try to deleting Berksfile.lock(take backup) and do berks install and then upload?
it should install all the necessary version of mysql cookbook
and then all the cookbook can have the mysql version they require

It sounds like you have something funky in your Berkfile or Policyfile. Check for unintended version pins there and possibly run berks update to force a re-solve. The user-specified-dependency means that constraint is coming from outside the cookbook system, usually via a manual cookbook 'mysql', '8.3.0' in a Berksfile but there are other ways like environment constraints or run list version hacks.

Related

stdlib module is not detected

I have a module which reportedly required puppetlabs-stdlib.
[vagrant#learn puppet]$ sudo /opt/puppetlabs/bin/puppet module list --tree --modulepath=/vagrant/puppet -v
Warning: Missing dependency 'puppetlabs-stdlib':
'elk' (v0.1.0) requires 'puppetlabs-stdlib' (>= 1.0.0)
/vagrant/puppet
└─┬ elk (v0.1.0)
└── UNMET DEPENDENCY puppetlabs-stdlib (>= 1.0.0)
I installed the module as follows:
[vagrant#learn puppet]$ sudo /opt/puppetlabs/bin/puppet module install puppetlabs-stdlib
Notice: Preparing to install into /etc/puppetlabs/code/environments/production/modules ...
Notice: Downloading from https://forgeapi.puppet.com ...
Notice: Installing -- do not interrupt ...
/etc/puppetlabs/code/environments/production/modules
└── puppetlabs-stdlib (v6.1.0)
However, the missing dependency is not resolved still. How can I make Puppet see the lib that I installed?
The command
sudo /opt/puppetlabs/bin/puppet module list --tree --modulepath=/vagrant/puppet -v
Reports that the elk module's dependency on puppetlabs-stdlib is unmet, whereas you show that module to be installed in /etc/puppetlabs/code/environments/production/modules. These facts are consistent.
The --modulepath argument to puppet module list does not do what you seem to expect. This might be more clear if you took into account Puppet's definition of the term:
The master service and the puppet apply command load most of their
content from modules found in one or more directories. The list of
directories where Puppet looks for modules is called the modulepath.
The modulepath is set by the current node's environment.
The modulepath is an ordered list of directories, with earlier
directories having priority over later ones. Use the system path
separator character to separate the directories in the modulepath
list. On *nix systems, use a colon (:); on Windows use a semi-colon
(;).
The --modulepath option to your puppet module list command expresses a complete modulepath, and accordingly, in your command it tells Puppet to consider only modules in /vagrant/puppet. With respect to that modulepath, the stdlib dependency is indeed unmet.
This alternative should show the dependency satisfied:
sudo /opt/puppetlabs/bin/puppet module list --tree \
--modulepath=/etc/puppetlabs/code/environments/production/modules:/vagrant/puppet -v
What significance either result has at Puppet runtime depends on the modulepath Puppet is using for catalog building. If you leave off the --modulepath option altogether then you will get results relevant to Puppet's current configuration and default environment, which might or might not be more relevant to your actual usage scenario.
You are not referencing the same modulepath.
In the first instance, you are listing modules from /vagrant/puppet as per your --modulepath statement.
When you install, you did not specify a modulepath, so as per std out of the install:
Notice: Preparing to install into /etc/puppetlabs/code/environments/production/modules
So you have to either point the stdlib install to the same path: puppet module --modulepath=/vagrant/puppet install puppetlabs-stdlib, or update the modulepath of the client to run off the default, /etc/puppetlabs/code/environments/production/modules

using berkshelf programmatically

i am managing my cookbooks with berkshelf.
i would like to play a little bit with the cookbook dependency graph. for instance:
given a cookbook, get its dependencies (the same as the resolver does it)
given a cookbook, get depended cookbooks (same as berks contingent)
i tried
require 'berkshelf'
Berkshelf::Lockfile.new(file_path: './Berksfile.lock')
but it did not work.
how can berkshelf be invoked programmatically under ruby and achieve the above?
This is not supported, or anything even in the same area code as supported. We have no public Ruby APIs for this data, sorry. You can look at the Solve gem, but it's not really used outside of our own use cases.
If you have Berksfile, It might be helpful for you.
require 'berkshelf'
# initializing berksfile
s = Berkshelf::Berksfile.from_file('./find_versions/Berksfile')
#install dependencies to create Berksfile.lock
s.install()
# parse lockfile
s.lockfile().parse()
# find dependencies
puts s.find('cookbook_name').locked_version

How to add conditional Gemspec dependencies

I have made a Gem that optionally logs with Sentry or Airbrake, depending on the ENV variables. I have these in my Gemspec but I only need one or the other:
spec.add_runtime_dependency "sentry-raven"
spec.add_runtime_dependency "airbrake"
Is there a way to only include the one that the end user needs, based on ENV variables?
You may be best off leaving off both dependencies and adding to your README that a user needs to add the dependency to their own Gemfile. I've seen some gems check to see what is defined, raising an error if they don't find anything, as well as allowing the user to configure in their application which tool to use.
No, this is not possible for the simple reason that the Gemspec is executed on your computer, not the user's computer. It is executed to build the Gem, not install it.
RubyGems does not have a sophisticated dependency system like e.g. dpkg/APT has: a Gem either depends on another Gem or it doesn't.

Parsing a Gemfile.lock with Bundler

Basically, I'm trying to build a gem that does some form of test failure when certain dependencies are outdated. In the testing framework I can easily stub the crap out of Gem so that Gem.loaded_specs("foo") returns the spec for the gem foo. What I'm looking for is a way to provide a fixture Gemfile and then parse the lock file.
When I use Gem.loaded_specs it somehow magically knows which Gemfile to use, how do I feed it a different gemfile (ie: my fixture)?
I know somebody mentioned "use bundler" and that makes sense but in my code for the gem I do this:
gem_here = Gem.loaded_specs[gem_name]
gem_here.nil? ? :not_in_bundle : gem_here.version.to_s
I need to make this work with my fixture Gemfile and not the current projects gemfile.
Does this make any sense? Sorry if this is vague.
Note: I'm not trying to do this via the CLI. I'd like to use programmatic (ie: ruby api) methods if I can.
Edit:
I'm going with this kind of source now:
gem_here = Bundler.load.specs.detect do |specs|
specs.name == gem_name
end
gem_here.nil? ? :not_in_bundle : gem_here.version.to_s
Parsing yourself isn’t desirable as Bundler does the heavy-lifting (parsing, dependency resolution).
Bundler::LockfileParser.new(Bundler.read_file(Bundler.default_lockfile))
Then, use gemspecs and the lockfile to visit all runtime and/or development dependencies. Runtime/development deps for specific gems are available via a (currently undocumented, needs contrib) RubyGems JSON API https://api.rubygems.org/api/v2/rubygems/rails/versions/5.0.0.1.json
Note: Bundler 2.0 Gemfile.lock -> gems.locked

How to specify version of specinfra for test-kitchen verify command?

Last version of https://github.com/serverspec/specinfra is broken (https://github.com/serverspec/specinfra/pull/229).
so when I execute:
kitchen verify
it doesn't verify and just throw some errors.
So I thought - How to specify some particular version of specinfra gem for test-kitchen?
then I could just specify previous version that worked and continue development.
I'm sure that this particular error will be fixed, but it would be great to know how to change versions of gems that test-kitchen uses.
Because it will happen again.
Unfortunately not super easily. The actual serverspec gem install is done on the target system via busser-serverspec. This code calls RubyGems directly, so it is hard to override. If I'm reading that #test hook correctly you may be able to include a Gemfile in your suite files that pulls in specinfra from git after the initial install.

Resources