[chef] Re: Re: Re: Re: Is there a difference between setting attributes like this?


Chronological Thread 
  • From: Lamont Granquist < >
  • To:
  • Subject: [chef] Re: Re: Re: Re: Is there a difference between setting attributes like this?
  • Date: Thu, 12 Feb 2015 19:04:31 -0800


I'd advise for starting out that you begin with a role_base cookbook with multiple recipes in it.  The structure then looks like:


metadata.rb
=======

name "role_base"
depends "openssh"
depends "resolver"

attributes/default.rb
=============

# ssh settings
default["openssh"]["server"]["port"] = 22

# resolver settings
default[:resolver][:nameservers] = [ "8.8.8.8", "8.8.4.4" ]

recipes/default.rb
===========

include_recipe "openssh"
include_recipe "resolver"
include_recipe "#{cookbook_name}::packages"

recipes/packages.rb
============

%w{lsof tcpdump zsh dmidecode}.each do |pkg|
  package pkg
end

I would still use an actual base role:

roles/base.json
==========
{
  "name": "base",
  "description": "It's all about the base",
  "run_list": [
    "recipe[role_base]"
  ]
}

This keeps things fairly tidy.  You still have a role in your run_list but its pretty much a no-op pointer to the role cookbook.  The role cookbook can serve multiple purposes and you should break it up into sub-recipes that each do one thing with a default recipe that runs them all.  You can start to break out other cookbooks from the base cookbook and make it all smaller and more composable, but I think you should do that as you decide that you start wanting to do more test-driven-development or one of the recipes starts to give you obvious pain.  One thing that you cannot do with this approach is have multiple base roles that share code between them.  You cant have a single cookbook with multiple roles in it (the attributes and libraries files don't really allow it -- whenever you include a cookbook you include all its attribute files so there no way to switch behavior there between different kinds of hosts).

For your apps, I'd have simpler roles, they should have one default recipe and should depend on library cookbooks that export LWRPs to set up applications.  You can use cookbooks like application_ruby to provide those, or you can roll your own LWRPs for "what it means to be an app at MYORG"

So:

cookbooks/role_foo/metadata.rb
====================

name "role_foo"
depends "application_ruby"
depends "build-essentials"

cookbooks/role_foo/attributes/default.rb
==========================

default['build-essential']['compile_time'] = true

cookbooks/role_foo/recipes/default.rb
========================

include_recipe "build-essentials"

package "libv8-dev"

# ... more stuff ...

application "foo" do
  path "/srv/foo"
  owner "root"
  group "root"
  rails do
     # ... stuff ...
  end
end

You'd probably call this an "application" cookbook, but its also a "wrapper" cookbook around the build-essentials cookbook, which I think is where the terminology can wind up tying your brain into knots.   The important point is that your application needs:

- the compiler toolchain installed (at compile_time for the sake of argument [*])
- the libv8-dev package installed
- a bunch of typical stuff you do to install and deploy a rails app in you organization

If you start duplicating a lot of code in your application cookbooks you can start to extract those out to either other cookbooks which may be recipes or may be LWRPs so that you might wind up with a 'base' application cookbook that all your application cookbooks depend upon and use include recipe:

cookbooks/app_base/metadata.rb
=====================

name "app_base"
depends "application_ruby"
depends "build-essentials"

cookbooks/app_base/attributes/default.rb
==========================

default['build-essential']['compile_time'] = true

cookbooks/app_base/recipes/default.rb
========================
include_recipe "build-essentials"

package "libv8-dev"

cookbooks/role_foo/metadata.rb
====================

name "role_foo"
depends "app_base"

cookbooks/role_foo/recipes/default.rb
========================

include_recipe "role_foo::default.rb"

application "foo" do
  [....stuff...]
end

As "app_base" grows larger you can keep your role_foo (and role_bar and role_baz) cookbooks smaller now.

And there's no pattern which you should definitely adopt.  For small needs what I've outlined here will be more than enough.  As you grow you probably need to refactor and wrap more.   At the same time, for my own use I wound up overengineering my small personal needs and had a few dozen cookbooks composing my base role and recently refactored them into this one-base-cookbook-many-recipes pattern which greatly simplified things for me.

I do suggest that your run_list still looks like 'role[base], role[foo]' and that those are independent.  That way you can build your app in a test harness and not have to install every user account and all their dotfiles that your base role should be responsible for -- the application role should have all of its dependencies and only its dependencies.  If you slurp your whole base role into your app cookbooks then you'll miss out on being able to quickly build apps in test harnesses.

HTH

[*] also I don't think you should ever need to force build-essentials to compile-time for apps that you install so maybe thats a bad example to pick, but I couldn't find a more relevant one offhand and it nearly makes sense that you might want to do that for native gem installs.

On 2/12/15 6:01 PM, Greg Barker wrote:
" type="cite">
Thanks for the feedback!

Regarding setting the attributes in recipe code like that, I got it from this blog post that cheeseplus linked me to in IRC. Today I've been trying to experiment with using Environment Cookbooks and Role Cookbooks but I'm having a hard time wrapping my head around it. Regular Environments & Roles make a lot of sense to me, and I understand some of the downsides to them. I like that I get a version number associated with the environment and role if they are moved to cookbooks. I'm just not clear to me if what I'm gonna end up with after this refactoring is ideal, or if it's just gonna be a mess of different types of cookbooks and simple roles that only reference the role cookbook they are associated with. Other questions arise along the way like, if I have a mycorp-base cookbook, do I need a mycorp_base role, or can I just have the mycorp-myapp-jenkins cookbook do include_recipe "mycorp-base" ? Do I keep the myapp_jenkins role and have the run_list set to role["mycorp-base"], recipe["mycorp-myapp-jenkins"] ?

I'm also a little unsure if things have changed enough since posts I'm basing this off like this one and this one were written that maybe there's other some best practice to follow now?

On Thu, Feb 12, 2015 at 5:37 PM, Lamont Granquist < " target="_blank"> > wrote:

Also you don't want to use node.set, you want to use node.default or maybe node.override if you have to.  When you use node.set that is an alias for node.normal which has the side effect of persisting data permanently in the node object.  If you delete those lines, you'll find the attributes are still set, which can be highly confusing.  The normal and override attribute precedence levels are actually wiped at the start of the chef run and re-built completely by your code which is what you actually want.  You should only use 'node.set' or 'node.normal' if you're doing something like generating a password for a database on the first run and you need to remember that (and even there you should probably use a data bag).

And one last thing is that its better to move those kinds of settings into an attributes file if at all possible and not set attributes in recipe code like that.


On 2/12/15 3:53 PM, Sean Clemmer wrote:
Yes.

The first version will replace the entire node.openssh.server attribute with the Hash you've provided. The second will only update node.openssh.server.port and node.openssh.server.permit_root_login. So if there were other attributes like node.openssh.server.example declared elsewhere, the first version would blow away this value, while the second would not.

On Thu, Feb 12, 2015 at 3:41 PM, Greg Barker < " target="_blank"> > wrote:
This is for my "base cookbook", wasn't sure if there was a difference between setting attributes like this:

node.set["openssh"]["server"] = {
  "port" => 22,
  "permit_root_login" => "no"
}

Versus setting them like this:
node.set["openssh"]["server"]["port"] = 22
node.set["openssh"]["server"]["permit_root_login"] = "no"

Thanks!







Archive powered by MHonArc 2.6.16.

§