Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Reprogramming Chef

Reprogramming Chef

Steven Danna

July 31, 2014
Tweet

Other Decks in Technology

Transcript

  1. package "nginx"! ! template "/etc/nginx.conf" do! source "nginx.conf.erb"! variables :doc_root

    => "/var/www"! notifies :restart, "service[nginx]"! end! ! service "nginx" do! action [:enable, :start]! end!
  2. package "nginx"! ! template "/etc/nginx.conf" do! source "nginx.conf.erb"! variables :doc_root

    => "/var/www"! notifies :restart, "service[nginx]"! end! ! service "nginx" do! action [:enable, :start]! end!
  3. package "nginx"! ! template "/etc/nginx.conf" do! source "nginx.conf.erb"! variables :doc_root

    => "/var/www"! notifies :restart, "service[nginx]"! end! ! service "nginx" do! action [:enable, :start]! end!
  4. package "nginx"! ! template "/etc/nginx.conf" do! source "nginx.conf.erb"! variables :doc_root

    => "/var/www"! notifies :restart, "service[nginx]"! end! ! service "nginx" do! action [:enable, :start]! end!
  5. package "nginx"! ! template "/etc/nginx.conf" do! source "nginx.conf.erb"! variables :doc_root

    => "/var/www"! notifies :restart, "service[nginx]"! end! ! service "nginx" do! action [:enable, :start]! end!
  6. packages = [ "emacs",! "tmux",! "irssi" ]! ! packages.each do

    |pkg|! package pkg do! action :install! end! end!
  7. Ohai Plugins Knife Plugins Start, Report, and Error Handlers Event

    Subscribers and Output Formatters Definitions LWRPs
  8. Ticket #1: The LDAP Flood Ohai is trying to pull

    in my Corporation’s entire LDAP directory on every run. Please help!
  9. Etc.passwd do |entry|! user_passwd_entry = Mash.new(:dir => entry.dir, ! !

    ! ! ! ! ! ! ! ! ! ! ! :gid => entry.gid, ! ! ! ! ! ! ! ! ! ! ! ! ! :uid => entry.uid, ! ! ! ! ! ! ! ! ! ! ! ! :shell => entry.shell, ! ! ! ! ! ! ! ! ! ! ! ! ! :gecos => entry.gecos)! user_passwd_entry.each_value {|v| fix_encoding(v)}! etc[:passwd][fix_encoding(entry.name)] = user_passwd_entry! end lib/ohai/plugins/passwd.rb
  10. static VALUE! passwd_iterate(void)! {! struct passwd *pw;! ! setpwent();! while

    (pw = getpwent()) {! rb_yield(setup_passwd(pw));! }! return Qnil;! }! ext/etc/etc.c
  11. “The getpwent() function returns a pointer to a structure containing

    the broken-out fields of a record from the password database (e.g., the local password file /etc/passwd, NIS, and LDAP)”
  12. provides 'etc'! ! etc Mash.new! etc[:passwd] = Mash.new! etc[:group] =

    Mash.new! ! File.open("/etc/passwd", "r") do |f|! f.each_line do |line|! etc[:passwd].merge!(parse_passwd_line(line))! end! end! ! File.open("/etc/group", "r") do |f|! f.each_line do |line|! etc[:group].merge!(parse_group_line(line))! end! end! ! # parse_passwd_line and parse_group_line not shown!
  13. Open Classes, Part I module StringExtensions! def leet(str=self)! str.gsub("e", "3").gsub("o",

    "0")! end! end! ! class String! include StringExtensions! end! ! puts "hello, world".leet!
  14. Open Classes, Part I module StringExtensions! def leet(str=self)! str.gsub("e", "3").gsub("o",

    "0")! end! end! ! String.send(:include, StringExtensions)! ! ! puts "hello, world".leet!
  15. Ticket #2: The ugly search I have a recipe that

    configures remote rsyslog. But a search for the syslog server isn’t returning what I expect. Can you help?
  16. What happens if the node running the recipe is the

    rsyslog-server, and this is the first run?
  17. rsyslog_server = search(:node, ! "role:rsyslog-server").sort.first! ! if rsyslog_server.nil? ! rsyslog_server

    = node! else! ! template "/etc/rsyslog.conf" do! variables :server => rsyslog_server['fqdn']! end!
  18. rsyslog_server = search(:node, ! "role:rsyslog-server").sort.first! ! if rsyslog_server.nil? && !

    node['roles'].include?('rsyslog-server')! rsyslog_server = node! else! Chef::Application.fatal!(‘No rsyslog-server found.')! end! ! template "/etc/rsyslog.conf" do! variables :server => rsyslog_server['fqdn']! end!
  19. “That is a bit better, but I the recipe is

    a lot uglier and I want to use a similar pattern in other cookbooks.”
  20. module YourCorp! module DSL! def node_with_role(role)! candidates = search(:node, "roles:#{role}").sort!

    candidates << node if node['roles'].include?(role)! if candidates.empty?! fail "No node found with role:#{role}"! else! candidates.first! end! end! end! end! ! Chef::Recipe.send(:include, YourCorp::DSL)!
  21. Open Classes, Part II class String! def upcase! # Keep

    your workstation locked!! "LOLOLOL"! end! end! ! puts "hello, world".upcase!
  22. Open Classes, Part II class String! alias_method :old_upcase, :upcase! def

    upcase! old_upcase + "!!!"! end! end! ! puts "hello, world".upcase!
  23. Ticket #3: The Poor Typist Every time I typo my

    nginx configuration template, the service fails to restart properly. Why can’t Chef help me?!
  24. package "nginx"! ! template "/etc/nginx.conf" do! source "nginx.conf.erb"! variables :doc_root

    => "/var/www"! notifies :restart, "service[nginx]"! end! ! service "nginx" do! action [:enable, :start]! end!
  25. package "nginx"! ! template "/etc/nginx.conf.temp" do! source "nginx.conf.erb"! variables :doc_root

    => "/var/www"! notifies :run, "bash[check-nginx-template]", :immediately! end! ! bash "check-nginx-template" do! command "nginx -t -c /etc/nginx.conf.temp"! notifies :create, "file[/etc/nginx.conf]", :immediately! end! ! file "/etc/nginx.conf" do! content lazy { ::File.read(“/etc/nginx.conf.temp”) }! action :nothing! notifies :restart, "service[nginx]", :immediately! end! ! service "nginx" do! action [:enable, :start]! end!
  26. # This should succeed! template "/tmp/foo" do! verify do |path|!

    true! end! end! ! # This should raise an error! template "/tmp/bar" do! verify do |path|! false! end! end!
  27. package "nginx"! ! template "/etc/nginx.conf" do! source "nginx.conf.erb"! variables :doc_root

    => "/var/www"! verify do |path|! `nginx -t -c #{path}`! $? == 0! end! notifies :restart, "service[nginx]"! end! ! service "nginx" do! action [:enable, :start]! end!
  28. class Chef::Resource::Template < Chef::Resource::File! def verify(&block)! if block_given? ! @verify_block=block!

    else! @verify_block! end! end! end! ! class Chef::Provider::File! alias_method :old_dcc, :do_contents_changes! def do_contents_changes! do_verify if !@new_resource.verify.nil?! old_dcc! end! ! def do_verify! Chef::Log.debug "Validating rendered file"! unless @new_resource.verify.call(tempfile.path)! raise Chef::Exceptions::ValidationFailed, "Could not validate rendered file"! end! end! end!
  29. On Reprogrammable System Customizing Chef Chef Sugar Passwd_min Ohai Plugin

    http://stackoverflow.com/a/4471202/618304 Stack Overflow Answer https://stevendanna.github.io/blog/2013/04/13/passwd-min-ohai-plugin/ https://sethvargo.github.io/chef-sugar/ http://shop.oreilly.com/product/0636920032984.do http://www.confreaks.com/videos/374-rubyconf2010-the-polite-programmer-s-guide ruby-etiquette Talk by Jim Weirich http://martinfowler.com/bliki/InternalReprogrammability.html