what makes a good cookbook?
DESCRIPTION
What makes a good Chef cookbook?TRANSCRIPT
![Page 1: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/1.jpg)
What Makes a Good Cookbook?Julian C. DunnSenior ConsultantChef Software, Inc.<[email protected]>
![Page 2: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/2.jpg)
Finding a Good Cookbook
![Page 3: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/3.jpg)
![Page 4: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/4.jpg)
Do judge a cookbook by its cover
![Page 5: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/5.jpg)
![Page 6: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/6.jpg)
![Page 7: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/7.jpg)
![Page 8: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/8.jpg)
![Page 9: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/9.jpg)
missing_attrs = %w{ postgres }.select do |attr| node['postgresql']['password'][attr].nil? end.map { |attr| "node['postgresql']['password']['#{attr}']" }
if !missing_attrs.empty? Chef::Application.fatal!([ "You must set #{missing_attrs.join(', ')} in chef-solo mode.", "For more information, see https://github.com/opscode-cookbooks/postgresql#chef-solo-note" ].join(' '))end
Too Clever for Its Own Good
![Page 10: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/10.jpg)
Chef::Application.fatal!([ "You must set #{missing_attrs.join(', ')} in chef-solo mode.", "For more information, see https://github.com/opscode-cookbooks/postgresql#chef-solo-note" ].join(' '))
Poking at Chef Internals
• Other abuses: Messing with run_context and run_state
![Page 11: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/11.jpg)
template "/etc/whatever.conf" do ... not_if { foo }end
Compile vs. Execute Errors
if foo template "/etc/whatever.conf" do ... endend
not the same thing as
![Page 12: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/12.jpg)
execute “yum install httpd” do not_if “rpm -qa | grep -x httpd”end
Not declarative
• Also, the Chef recipe with 100 bash resource declarations
![Page 13: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/13.jpg)
execute "I will run every time" do action :run # because I don't have a guard hereend
Missing guards
![Page 14: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/14.jpg)
Fear of LWRPs• Missed abstraction opportunities
• No good example to put here; they’re all 200 lines long (thus proving my point)
![Page 15: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/15.jpg)
remote_file 'whatever.tar.gz' do source 'http://hardcoded.url.com/’end
Hardcoded Strings
![Page 16: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/16.jpg)
Excess Conditions & Recipe Length• https://github.com/opscode-cookbooks/mysql/blob/v3.0.12/recipes/server.rb
• (We’ve since refactored this)
![Page 17: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/17.jpg)
Good Cookbooks...
![Page 18: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/18.jpg)
Put control flow in attributes• Especially for cross-platform cookbooks
• Set common set of attributes, write common behavior in recipe context
![Page 19: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/19.jpg)
case node['platform']
when "debian", "ubuntu" default['postgresql']['client']['packages'] = %w{postgresql-client libpq-dev} default['postgresql']['server']['packages'] = %w{postgresql} default['postgresql']['contrib']['packages'] = %w{postgresql-contrib}
when "fedora", "amazon" default['postgresql']['client']['packages'] = %w{postgresql-devel} default['postgresql']['server']['packages'] = %w{postgresql-server} default['postgresql']['contrib']['packages'] = %w{postgresql-contrib} default['postgresql']['server']['service_name'] = "postgresql"
when "redhat", "centos", "scientific", "oracle" default['postgresql']['version'] = "8.4" default['postgresql']['dir'] = "/var/lib/pgsql/data"
if node['platform_version'].to_f >= 6.0 default['postgresql']['client']['packages'] = %w{postgresql-devel} default['postgresql']['server']['packages'] = %w{postgresql-server} default['postgresql']['contrib']['packages'] = %w{postgresql-contrib} else default['postgresql']['client']['packages'] = ["postgresql#{node['postgresql']['version'].split('.').join}-devel"] default['postgresql']['server']['packages'] = ["postgresql#{node['postgresql']['version'].split('.').join}-server"] default['postgresql']['contrib']['packages'] = ["postgresql#{node['postgresql']['version'].split('.').join}-contrib"] end default['postgresql']['server']['service_name'] = "postgresql"
end
Control Flow in Attributes
![Page 20: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/20.jpg)
node['postgresql']['server']['packages'].each do |pg_pack|
package pg_pack
end
Common Recipe Code
• Easy to support more platforms without modifying recipe
![Page 21: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/21.jpg)
default.rbdefault.rbdefault.rbdefault.rb
server.rbserver.rbserver.rbserver.rb
_suse.rb_suse.rb_suse.rb_suse.rb_fedora.rb_fedora.rb_fedora.rb_fedora.rb_windows.rb_windows.rb_windows.rb_windows.rb
Separate recipes by OS• If things you do are very different per platform, separate them into different recipes
![Page 22: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/22.jpg)
“Public” versus “Private” recipes• ‘_’ faux-namespacing
![Page 23: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/23.jpg)
loaded_recipes = if run_context.respond_to?(:loaded_recipes) run_context.loaded_recipes else node.run_state[:seen_recipes] end
node['mysql']['client']['packages'].each do |name| resources("package[#{name}]").run_action(:install)end
Do not abuse compile-time
•.run_action(:must_die)
• Use sparingly, if at all!
![Page 24: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/24.jpg)
if some_error_condition fail "Helpful error message" # rather than Chef::Application.fatal!("error")end
Avoid poking Chef Internals
•Chef::Application.fatal is for use by Chef itself
•fail or raise is better
![Page 25: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/25.jpg)
Attributes only where necessary• “Let’s create a node attribute for each of the 15,000 tunables in this daemon”
• Not necessary if you never touch 14,975 of those knobs
![Page 26: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/26.jpg)
git clone git://github.com/foozolix/foozolix.git./configuremakemake install
Give people options for installation
• Sigh.
• At least give people a way to install from packages.
• “Compile from source” should be banned in most cases.
![Page 27: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/27.jpg)
Be declarative• Know and use built-in Chef resources
• Know where to find LWRPs to avoid batch/execute/powershell_script
• Consider using log resource versus Chef::Log
• Shows up in reporting as an updated resource instead of having to trawl through client.log
• Set an idempotency guard!
• Log at the right log level
![Page 28: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/28.jpg)
node['jboss']['instances'].each do |instance| link "/etc/init.d/#{instance['name']}" do to "/etc/init.d/jbossas" end
template "/etc/sysconfig/#{instance['name']}" do source "jbossas.sysconfig.erb" owner node['jboss']['server']['user'] group node['jboss']['server']['group'] mode "00644" variables( :jbossconf => instance['name'] ) action :create end
template "#{node['jboss']['server']['home']}/bin/standalone.sh" do source "standalone.sh.erb" owner node['jboss']['server']['user'] group node['jboss']['server']['group'] mode "00755" action :create end link "#{node['jboss']['server']['home']}/bin/#{instance['name']}.sh" do to "#{node['jboss']['server']['home']}/bin/standalone.sh" endend
Repetition == LWRP Candidate
![Page 29: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/29.jpg)
actions :create, :delete
attribute :instance_name, :kind_of => String, :name_attribute => trueattribute :console_log_level, :kind_of => String, :required => trueattribute :datasources, :kind_of => Hash, :default => {}
.
.
.
default_action :create
Repetition == LWRP Candidate• Perfect for abstracting!
• Resource interface:
![Page 30: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/30.jpg)
jboss_instance "petstore" do instance_name "can_haz_cheezburgerz" console_log_level "DEBUG" datasources {'db1' => 'jdbc://whatever:5432/db1'}end
Repetition == LWRP Candidate
• Write/debug hard logic once
• Clear consumer interface
• Parameter validation & sanity checking
• Non-JBoss experts can invoke without knowing gnarly details
![Page 31: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/31.jpg)
module MyCookbook module Helper
# returns Windows friendly version of the provided path, # ensures backslashes are used everywhere def win_friendly_path(path) path.gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR) if path end end end
Write helper libraries
• Create reusable helper functions in pure Ruby
• Move repetitive detail out of recipe context.
![Page 32: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/32.jpg)
Keep Recipes Small• < 100 lines
• If longer than this, consider breaking up functionality
• Example: nagios server recipe does too much
• Installs Nagios
• Configures Nagios
• Pokes around in data bags for config items
![Page 33: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/33.jpg)
Use Community Helpers• Chef Sugar - http://code.sethvargo.com/chef-sugar/
• Chef Cutlery - https://github.com/realityforge/chef-cutlery
• You can also crib ideas if you want to avoid external dependencies
![Page 34: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/34.jpg)
Wrap-Up
![Page 35: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/35.jpg)
Testing• I didn’t mention testing once in this talk!
• I’m assuming you will write tests for your cookbooks.
• A whole other talk...
• ... including good/bad things to test
![Page 36: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/36.jpg)
Make your code aromatic• Keep recipes small
• Keep recipes simple
• Use a consistent style
• Use Foodcritic
![Page 37: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/37.jpg)
Beware Expertise Bias• Hide gnarly details from recipe context
• Libraries
• LWRPs
• Attributes
• Resist urge to be overly clever - not everyone’s an expert
• Akin to the one-line sed/awk script
• http://tinyurl.com/the-expertise-bias
![Page 38: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/38.jpg)
Learn from Software Developers• Everything I told you about information hiding, design patterns, testing, etc.
• Ops can learn from devs as well!
• Maybe we should call it OpsDev...
![Page 39: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/39.jpg)
Thank You!E: [email protected]: https://github.com/juliandunnT: @julian_dunnW: www.juliandunn.net
![Page 40: What Makes a Good Cookbook?](https://reader036.vdocuments.mx/reader036/viewer/2022070304/54c688c94a795949318b4598/html5/thumbnails/40.jpg)