Cookbook dependencies based on chef version - ruby

I have a wrapper cookbook that uses resources from other cookbooks, but they have different constraints. I tried defining my metadata.rb as follow :
name 'yp_linko'
maintainer 'The Authors'
maintainer_email 'devops#yp.ca'
license 'all_rights'
description 'Installs/Configures yp_linko'
long_description 'Installs/Configures yp_linko'
version '1.3.4'
depends 'apt'
if chef_version '<= 12' then
depends 'ypg_tomcat'
else
depends 'yp_tomcat'
end
This didn't work as chef grabbed both cookbooks during convergence. I tried a couple different syntax (only_if, unless, etc...) and none worked. Anyone has ideas to work around this issue?

TL;DR
You're using a constraint from the Chef DSL in a way that it's not meant to be used. You need to branch based on a Boolean expression, not a metadata constraint. I provide a couple of ways to do that using String#to_f as examples (not recommended if you care about patch levels in semantic versioning), as well as the more accurate but often-overlooked Gem::Version.
Don't Use a Constraint for Branching
if chef_version '<= 12'
You're trying to use a constraint from the DSL. This constraint has a specific purpose: to declare the chef-client versions supported by the cookbook, not to provide logical branching. Without looking at the underlying Chef code for the DSL, I'd say it's unlikely that the expression is feeding your if-then expression the way you expect. Setting aside for the moment whether this is the right pattern to follow at all, you can try grabbing the current version of your Chef tools in a couple of different ways:
Branching on the chef-client version, likely cast as a Float (it's normally a String). For instance:
if Chef::VERSION.to_f <= 12
Get the node value from ohai with something like:
if node['chef_packages']['chef']['version'].to_f <= 12
Parse the value directly from the client, e.g.:
depends %x(chef-client --version).split[1].to_f <= 12 ? 'ypg_tomcat' : 'yp_tomcat'
However, in all cases, you're going to have to deal with the fact that you're being passed a String containing semantic versioning, not a Float or Integer. So, you'll have to figure out how you really want to parse the information, which is potentially error-prone (see below for a trick using Gem::Version, though). In any case, once you've parsed it the way you want, you can match it using a comparison operator to get the branching behavior you want.
Better Options
Rather than trying to make the metadata constraint hold business logic, you should probably move the data out to an attribute. Consider an attribute such as node['yp_linko']['tomcat_cookbook'], which you can set based on some other detectable node value other than semantic versioning.
Another approach would be to declare both cookbooks a dependency, and then include the one you want inside a recipe in your yp_linko cookbook. For example, assuming you haven't declared incompatible chef-client versions in the Tomcat cookbooks:
# metadata.rb
depends 'yp_tomcat'
depends 'ypg_tomcat'
# default.rb
if Chef::VERSION <= Gem::Version.new(12)
include ypg_tomcat::default
else
include yp_tomcat::default
end
And finally, you should consider whether it really makes sense to be running different versions of your Chef clients within the infrastructure in the first place. There may be a business need to do that, but it's the real problem you're actually trying to solve for. Branching in your cookbooks is an X/Y solution to an infrastructure issue. It seems likely that you'd have other cookbooks with similar issues, so it's at least worth considering whether it makes more sense to get all your clients on the same version rather than solving the problem at the cookbook level.

Related

How to have multiple gems that share the same common Ruby module?

I've written a Ruby module, common_services.rb, which contains convenience methods for interfacing with a web service; I consider it a piece of common code. Now, I would like to create two separate gems, both of which utilize the convenience methods in this module. I visualize something like this:
rubyStuff/
commonCode/
common_services.rb <-- common code lives here
gemA/
bin/
lib/ <-- gemA would like to pull in common_services to use it
gemA.gemspec
gemB/
bin/
lib/ <-- gemB would also like to pull in common_services to use it
gemB.gemspec
Each of gemA and gemB would have a reference to the common code in order to use it, for example rubyStuff/gemA/lib/gemA/main.rb might begin as:
require_relative '../../commonCode/common_services.rb'
puts CommonServices.getMessageOfTheDay()
<etc>
It's not working out too well for me, so I'm wondering if there is a better way to do this in Ruby. One problem I'm having is with packaging a gem to reference a file that is outside of its own path:
s.files = Dir['bin/**'] + Dir['lib/**/*'] + Dir['../commonCode/**']
When using the .. notation in the gemspec, and trying to install the resulting gem, produces:
ERROR: While executing gem ... (Gem::Package::PathError) installing into parent path /Users/me/.rvm/gems/ruby-2.0.0-p247/gems/commonCode/common_services.rb of /Users/me/.rvm/gems/ruby-2.0.0-p247/gems/gemA-0.0.1 is not allowed
It makes sense to me why gems are not allowed to reach outside of their own path, but I'm not sure how to resolve this issue. In Ruby, how and where should I organize Ruby code that will be used by multiple gems in my codeline, so that the multiple gems can use this common code? Am I going about reuse entirely the wrong way? Symbolic link magic?
Thanks for taking the time to read through this.
Summarizing comments above for posterity...
There are a couple things to consider here.
1. Is commonCode significant? Is it a good chunk of code, or just a few files?
2. Are gemA and gemB significant? Should each of them really be gems?
Seems like you're in either one of two situations, depending on the significance of the code you've written:
commonCode is significant enough (amount of code, complexity, logical separation from gemA and gemB) to merit its own gem? If so, definitely pull it out into its own gem. You can require gem-common in the other two. It doesn't make sense to require a plain ruby file in A and B that lives outside of those two code bases.
commonCode is too insignificant to be pulled into its own gem. If this is the case, you should consider whether or not gemA and gemB really deserve their own gems, each. If they aren't big enough or logically separate enough, maybe all three can be combined into a larger gem.

What is the canonical YAML naming style

I am designing a new YAML file, and I want to use the most standard style of naming. Which is it?
Hyphenated?
- job-name:
...
lower_case_with_underscores?
- job_name:
...
CamelCase?
- jobName:
...
Use the standard dictated by the surrounding software.
For example, in my current project the YAML file contains default values for Python attributes. Since the names used in YAML appear in the associated Python API, it is clear that on this particular project, the YAML names should obey the Python lower_case_with_underscores naming convention per PEP-8.
My next project might have a different prevailing naming convention, in which case I will use that in the associated YAML files.
Kubernetes using camelCase: https://kubernetes.io/docs/user-guide/jobs/
apiVersion, restartPolicy
CircleCI using snake_case: https://circleci.com/docs/1.0/configuration/
working_directory restore_cache, store_artifacts
Jenkins with dash-case: https://github.com/jenkinsci/yaml-project-plugin/blob/master/samples/google-cloud-storage/.jenkins.yaml
stapler-class
So it looks like projects and teams use their own conventions and there is no one definite standard.
A less popular opinion derived from years of experience:
TL;DR
Obviously stick to the convention but IMHO follow the one that is established in your project's YML files and not the one that comes with the dependencies. I dare to say naming convention depends on too many factors to give a definitive answer or even try to describe a good practice other than "have some".
Full answer
Libraries might change over time which leads to multiple naming conventions in one config more often than any sane programmer would like - you can't do much about it unless you want to introduce (and later maintain) a whole new abstraction layer dedicated to just that: keeping the parameter naming convention pristine.
A one example of why you would want a different naming convention in your configs vs. configs that came with the dependencies is searchability, e.g. if all dependencies use a parameter named request_id, naming yours request-id or requestId will make it distinct and easily searchable while not hurting how descriptive the name is.
Also, it sometimes makes sense to have multiple parameters with the same name nested in different namespaces. In that case it might be justified to invent a whole new naming convention based on some existing ones, e.g.:
order.request-id.format and
notification.request-id.format
While it probably isn't necessary for your IDE to differentiate between the two (as it's able to index parameters within the namespace) you might consider doing so anyway as a courtesy for your peers - not only other developers who could use different IDEs but especially DevOps and admins who usually do use less specialized tools during maintenance, migrations and deployment.
Finally, another good point raised by one of my colleagues is that distinctive parameter names can be easily converted into a different convention with something as simple as one awk command. Doing so the other way around is obviously possible but by an order of magnitude more complicated which often spawns debates in the KISS advocates community about what it really means to "keep it simple stupid".
The conclusion is: do what's most sensible to you and your team.

Advice on how/where to place requires in own gem

I am going through the blog Creating mountable Gem:
Its important to note that you should require your dependent gems explicitly in the root file of your gem. Say if your gem is named my_cool_gem, then you should have my_cool_gem.rb created inside lib folder. If your gem is dependent on strong_parameters, then you need to add these lines:
The author did not mention why it is required to have the dependent gems explicitly in the root file. Can some one explain why this needs to be done?
It's just cleaner. What if you had require statements in various files scattered all over your gem? It'd be a mess to easily see all the dependencies.
This statement:
Its important to note that you should require your dependent gems
explicitly in the root file of your gem.
It is not a strict statement that you have to conform to (to be fair to the author, the word is "should", not "must"). Some people prefer to take a different approach, and e.g. require dependencies only in parts of the library that use them.
However, you do need to consider:
Execution order of require statements that define symbols used elsewhere. You cannot call a DSL method that sets up instance methods for you, if the gem that provides the method has not yet been require-d
You should check that you really do require all correct the dependencies, and your gem functions correctly in projects that do not load them already.
With both of these in mind, it is often quickest and easiest to require dependencies early and and in a way that can be quickly viewed. So the advice is sound; as it was presented in a very short article, covering the statement with caveats and extra background would make the whole thing a lot less pithy.

Cross version line matching

I'm considering how to do automatic bug tracking and as part of that I'm wondering what is available to match source code line numbers (or more accurate numbers mapped from instruction pointers via something like addr2line) in one version of a program to the same line in another. (Assume everything is in some kind of source control and is available to my code)
The simplest approach would be to use a diff tool/lib on the files and do some math on the line number spans, however this has some limitations:
It doesn't handle cross file motion.
It might not play well with lines that get changed
It doesn't look at the information available in the intermediate versions.
It provides no way to manually patch up lines when the diff tool gets things wrong.
It's kinda clunky
Before I start diving into developing something better:
What already exists to do this?
What features do similar system have that I've not thought of?
Why do you need to do this? If you use decent source version control, you should have access to old versions of the code, you can simply provide a link to that so people can see the bug in its original place. In fact the main problem I see with this system is that the bug may have already been fixed, but your automatic line tracking code will point to a line and say there's a bug there. Seems this system would be a pain to build, and not provide a whole lot of help in practice.
My suggestion is: instead of trying to track line numbers, which as you observed can quickly get out of sync as software changes, you should decorate each assertion (or other line of interest) with a unique identifier.
Assuming you're using C, in the case of assertions, this could be as simple as changing something like assert(x == 42); to assert(("check_x", x == 42)); -- this is functionally identical, due to the semantics of the comma operator in C and the fact that a string literal will always evaluate to true.
Of course this means that you need to identify a priori those items that you wish to track. But given that there's no generally reliable way to match up source line numbers across versions (by which I mean that for any mechanism you could propose, I believe I could propose a situation in which that mechanism does the wrong thing) I would argue that this is the best you can do.
Another idea: If you're using C++, you can make use of RAII to track dynamic scopes very elegantly. Basically, you have a Track class whose constructor takes a string describing the scope and adds this to a global stack of currently active scopes. The Track destructor pops the top element off the stack. The final ingredient is a static function Track::getState(), which simply returns a list of all currently active scopes -- this can be called from an exception handler or other error-handling mechanism.

What are the things you would like improved in the Ruby language?

What are the things you wish Ruby (and more generally the Ruby community) would improve?
I read somewhere that Ruby is the love-child of Smalltalk and LISP, with Miss Perl as the Nanny.
I have a lot of respect for Ruby's parents, but I'm not sure I like the influence Miss Perl had on the child. Specifically, I don't like the predefined variables: I need a cheat sheet to know what they mean. You could say "just don't use them". Well, I don't... but other people do. And when I download a plugin on the Web, I have no choice but to fetch my cheat-sheet if I ever need to go and touch the source code. I just wish they would remove those from the language itself.
Also, I think that Ruby is too much of a moving target. My code breaks on every new Ruby upgrade, even on minor releases. This is true also of Ruby on Rails and most Rails plugins I have worked with: they just change all the time, and nobody seems to care whether the changes break everything or not. IMHO, although I love a lot of things in Ruby, this lack of stability is almost a show-stopper.
I wish people would consider backward compatibility between minor releases as an unbreakable rule when releasing a new language (or library or framework) version.
I wish that some of the lesser used modules of the standard library were documented.
Make require-ing files less painful. Don't ask me how, but maybe have one file dedicated to knowing the paths involved and just get rid of the relative path crud from everything else.
Getting rid of the artificial distinction between Modules and Classes would be nice.
Both Modules and Classes are Namespaces. Modules are also Mixins, while Classes aren't. Classes can also be instantiated while Modules can't. This distinction is unnecessary. Just get rid of Modules and allow Classes to be used as Mixins.
An example of a language where this works is Newspeak.
I'd appreciate being able to install ruby 1.9 as an RPM rather than having to use the source.
Make Ruby completely Message Sending based, get rid of everything that is not a message send: local variables, global variables, instance variables, class hierarchy variables, constants, magic globals, magic constants, builtin operators, builtin keywords, even literals. See Self, Ioke or Newspeak for the incredible power and elegance this gains.
I wish they would get rid of the predefined variables: $!, $&, $+, etc.
I would like to have support for static compile-time metaprogramming. The Converge Programming Language might be a good starting point.
Replace the Mixin system with a Traits system.
Replace Exceptions with a Common Lisp style Conditions system.

Resources