📜 ⬆️ ⬇️

Berkshelf and Chef cookbook dependencies

Hi, Hub users!
I continue my immersion in the piquancy of automation and configuration management , in parallel trying to share my experience with the community .

It will be discussed again about the automation tool for resolving dependencies of the Chef cookbooks that our company uses, namely, Berkshelf .


And what have Berkshelf?


Chef has a fairly large and growing community, which constantly makes its contribution to the creation and editing of cookbooks . All of them are stored on the Community site, many of them are very often used by us.
')
However, there is one “but” in our company associated with editing the community cookbooks . The correct way to make any changes to those related to the specifics of the company's infrastructure is to write wrapper- containing changes (for example, reassigning attributes, changing recipes, etc.). In short, the process of writing a wrapper is described in this article .

But, having the right path does not mean that everyone will follow it. That is why in the repository of the cookbooks of our company there turned out to be a lot of community cookbooks with unfair edits. And it's time to clean the repository :)

The plan was as follows:

- select the community cookbook and in our repository;
- assess the presence and number of local edits ( diff between identical versions of cookbooks );
- make changes to the wrapper and include a call to the community cookbook or recipe from it;
- remove the community cookbook and add it to the wrapper dependencies.

And what to do with the "cleaned" community cookbooks ? How after removal - will they appear “clean” on the Chef Server ?

Just here Berkshelf comes to the rescue .

What is Berkshelf and what is it eaten with?



Berkshelf is a dependency manager for Chef cookbooks . It is written in Ruby and has methods and APIs for interacting with the Chef Server .
The Berkshelf installation comes from Ruby Gems either through the use of Chef DK .
We are using Berkshelf version 2.0 , however, version 3.0 was recently released, which brought some changes and “buns”.
If you decide to use Ruby Gem - I advise you to install it within the framework of Ruby installed by Chef Server (the executable files are on the path - / opt / chef / embedded / bin / ).
To interact with Chef Server, you must configure Berkshelf, indicating to it the address of our server and certificates for authorization on it. To configure, you must run the following command:
berks configure 

The configuration files are in one of the following paths:
 $PWD/.berkshelf/config.json $PWD/berkshelf/config.json $PWD/berkshelf-config.json $PWD/config.json ~/.berkshelf/config.json 

After you have correctly installed Berkshelf , you can proceed to resolving dependencies for cookbooks .
For this, Berkshelf first copies the cookbook and their dependencies to their shelf (local storage / repository Berkshelf , the default is ~ / .berkshelf / cookbooks / ), and then uploads everything to the Chef Server .
Immediately there are questions: " How does Berkshelf know about dependencies? ", " What does he do to resolve them? "
Berkshelf instructions are contained in the Berksfile , located in the root directory of the cookbook . This file is created using the command
 berks init 
in the root directory.
The contents of this file describe the dependencies and the source for resolving them. An example of such a file:

 site :opscode metadata cookbook 'my-cookbook', (:path | :git | :github) cookbook 'my-book-2', ('> 1.0.0') 


This file is prescribed as follows:
- take into account the dependencies from the metadata.rb file of our cookbook and download them from the Opscode Community site;
- my-cookbook will be loaded along the path specified in brackets (this can be either a local path or a link to Git );
- The my-book-2 of the latest version, higher than 1.0.0, will be downloaded from the Opscode Community site.

After that, you can run
 berks install 
and wait for the successful copying of dependencies to the Berkshelf local storage. As a result, the storage directory should contain all the cookbooks, and those mentioned in the depends field of the metadata.rb file, their dependencies (dependencies), and the two cookbooks mentioned in the Berksfile . You can verify the result with the command
 berks shelf list 

The next step is to launch
 berks upload 
which will download everything from local storage to the Chef Server . As a result (provided that the Chef Server is available and we can log in using the certificate files) - all dependencies will be resolved. The result of the download can be checked with the command
 knife cookbook list 
the output of which should contain new cookbooks -and

Essentially, this is the whole process of basic use of Berkshelf . Of course, this is not all the functionality, because Berkshelf interaction with Vagrant , Chef Solo , Chef Client , and also some innovations of version 3.0 are not mentioned. However, I think this is enough for most.

Our history of using Berkshelf


Everything would be fine if again not one “but” - Berkshelf does not know how to work cyclically. I am very surprised, because I did not find an option in which he could " natively " take into account the nesting of directories with a cookbook , they are a kind of nested mode. What I mean?
For example, imagine a typical situation, there is a chef-repo directory in which the cookbooks directory is embedded, which contains our cookbooks ( ./chef-repo/cookbooks/ ).
In the current version of Berkshelf - it is necessary to go to each of the cookbook directories and execute commands related to Berkshelf in it . That is - a bash script that would do it, not with your own hands, in fact ... This is a solution, but alas - not the best, frankly speaking - “a crutch”.
On the Internet, an article was found in which using Ruby-code, this issue was solved.
I will give the code of our " root " Berksfile, located in the directory ./chef-repo/ :

 site :opscode metadata def dependencies(path) berks = "#{path}/Berksfile" instance_eval(File.read(berks)) if File.exists?(berks) end Dir.glob('./cookbooks/*').each do |path| dependencies path cookbook File.basename(path), :path => path end cookbook 'gecode', '= 2.1.0' 


In fact, this piece of code is going to the contents of all the directories of cookbooks (their metadata.rb and Berksfile ) in the “ root ” Berksfile.

We successfully applied the proposed solution and voila - all the cookbooks were on our Chef Server .
However, in the mentioned article one big feature was not mentioned - the Berksfile format of our cookbooks .
Experimentally, it was found that they should look like this:
 group :name_of_cookbook do cookbook 'my-cookbook', (:path | :git | :github) cookbook 'my-book-2', ('> 1.0.0') end 

That is, a list of cookbooks that must meet certain conditions — for example, a version or location that is enclosed in a group (so that the same dependencies of different cookbooks do not cause multiple entry conflicts). All other dependencies are taken from the file metadata.rb . Under the cut - a specific example, so that it was clearer:
Hidden text
 group :database do cookbook 'postgresql', '= 3.3.4' cookbook 'aws', :path => './cookbooks/aws' cookbook 'xfs', :path => './cookbooks/xfs' cookbook 'mysql', '= 4.1.2' end 


As a result, for the cookbook database, 4 cookbooks will be downloaded that meet certain conditions, as well as all the other dependencies mentioned in metadata.rb , as well as the dependencies mentioned (for excuse the tautology).
All that remains to be done after editing the Berksfiles of our cookbooks is to run the commands in the ./chef-repo directory .
 berks install && berks upload 

Here is a solution, perhaps not very reliable or convenient - but a solution. Worked with our repositories several times successfully.
If someone came across this issue and can share experience - write comments or PM.

Thank you all for your attention, to new articles.

PS A colleague says that Berkshelf v.3.0 is broken, so we do not recommend it!

Source: https://habr.com/ru/post/221791/


All Articles