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

Becoming Fearlessly Definite & Resourceful

Becoming Fearlessly Definite & Resourceful

Doing everything in chef cookbooks and definitions is easy and powerful. But I'll show you when it's time to set aside them and bring out the heavyweight guns: full fledged resources. I'll show you how we've taken our (surprisingly complicated) application deployment and database management definitions and converted them to heavyweight resources. Some things that are better: input validation, error handling, debugging information, testing, readability (really!) and ease of future improvements. Most importantly, I'll show that sometimes heavyweight resources are easier than LWRPs.

Transcript

  1. Becoming fearlessly definite & resourceful

  2. Infrastructure Engineer

  3. I keep the series of tubes flowing

  4. I keep the business value flowing

  5. What business value?

  6. Increase revenue

  7. Decrease expenses Increase revenue

  8. Decrease expenses Increase revenue Increase perceived value

  9. Decrease expenses Increase revenue Increase perceived value Increase throughput

  10. Decrease expenses Increase revenue Increase perceived value Decrease latency Increase

    throughput
  11. Decrease expenses Increase revenue Increase perceived value Decrease latency Increase

    throughput Decrease availability
  12. Decrease non-utilization Decrease waiting Decrease false negatives Decrease job size

    Decrease low-pri work Decrease MTTR
  13. How?

  14. Change

  15. Change what?

  16. None
  17. Cognitive → Linguistic

  18. → Language Thought

  19. Change information

  20. Change ideas

  21. Change words

  22. Change language

  23. We have the tools

  24. • Pure Recipes • Definitions • Lightweight Resource/Providers • Heavyweight

    Resource/Providers
  25. A tool isn’t trusted until it is used in desperation

  26. None
  27. Starting up

  28. One app One service One recipe Starting Up

  29. include_recipe 'mysql'! include_recipe 'apache2'! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'!

    ! group 'adm'! ! user 'awesome' do! group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "/srv/awesome/shared/#{dir}" do! owner 'awesome'! group 'adm'! mode '0750'! end! end! ! template '/srv/awesome/shared/config/database.yml' do! source 'database.yml.erb'! owner 'awesome'! group 'adm'! mode '0640'! variables(! database: {! host: 'awesome-db.awsm.net',! adapter: 'mysql2',! database: 'awesome',! username: 'awesome',! password: 's3kr3t'! },! rails_env: node.chef_environment! )! end! ! deploy '/srv/awesome' do! repo 'git@github.com/awsm/awesome'! user 'awesome'! group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user 'awesome'! environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user 'awesome'! environment new_resource.environment! end! end! end! ! web_app 'awesome' do! docroot '/srv/awesome/current/public'! template 'awesome.conf.erb'! server_name "awesome.#{node['domain']}"! server_aliases [ 'awesome', node['hostname'] ]! rails_env node.chef_environment! end
  30. One app One service One recipe Starting up Much Viable

    Product !
  31. Two apps Two services Two recipes

  32. include_recipe 'mysql'! include_recipe 'apache2'! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'!

    ! group 'adm'! ! user 'awesome' do! group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "/srv/awesome/shared/#{dir}" do! owner 'awesome'! group 'adm'! mode '0750'! end! end! ! template '/srv/awesome/shared/config/database.yml' do! source 'database.yml.erb'! owner 'awesome'! group 'adm'! mode '0640'! variables(! database: {! host: 'awesome-db.awsm.net',! adapter: 'mysql2',! database: 'awesome',! username: 'awesome',! password: 's3kr3t'! },! rails_env: node.chef_environment! )! end! ! deploy '/srv/awesome' do! repo 'git@github.com/awsm/awesome'! user 'awesome'! group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user 'awesome'! environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user 'awesome'! environment new_resource.environment! end! end! end! ! web_app 'awesome' do! docroot '/srv/awesome/current/public'! template 'awesome.conf.erb'! server_name "awesome.#{node['domain']}"! server_aliases [ 'awesome', node['hostname'] ]! rails_env node.chef_environment! end include_recipe 'mysql'! include_recipe 'apache2'! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'! ! group 'adm'! ! user 'spiffy' do! group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "/srv/spiffy/shared/#{dir}" do! owner 'spiffy'! group 'adm'! mode '0750'! end! end! ! template '/srv/spiffy/shared/config/database.yml' do! source 'database.yml.erb'! owner 'spiffy'! group 'adm'! mode '0640'! variables(! database: {! host: 'spiffy-db.awsm.net',! adapter: 'mysql2',! database: 'spiffy',! username: 'spiffy',! password: 's3kr3t'! },! rails_env: node.chef_environment! )! end! ! deploy '/srv/spiffy' do! repo 'git@github.com/awsm/spiffy'! user 'spiffy'! group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user 'spiffy'! environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user 'spiffy'! environment new_resource.environment! end! end! end! ! web_app 'spiffy' do! docroot '/srv/spiffy/current/public'! template 'spiffy.conf.erb'! server_name "spiffy.#{node['domain']}"! server_aliases [ 'spiffy', node['hostname'] ]! rails_env node.chef_environment! end
  33. +-- 2 lines: include_recipe 'mysql'----------------------------------------------------------------------------------------------------- ------------------! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'!

    include_recipe 'ruby'! ! group 'adm'! ! user 'awesome' do ! group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "/srv/awesome/shared/#{dir}" do ! owner 'awesome' ! group 'adm'! mode '0750'! end! end! ! template '/srv/awesome/shared/config/database.yml' do ! source 'database.yml.erb'! owner 'awesome' ! group 'adm'! mode '0640'! variables(! database: {! host: 'awesome-db.awsm.net', ! adapter: 'mysql2',! database: 'awesome', ! username: 'awesome', ! password: 's3kr3t'! },! rails_env: node.chef_environment! )! end! ! deploy '/srv/awesome' do ! repo 'git@github.com/awsm/awesome' ! user 'awesome' ! group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user 'awesome' ! environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user 'awesome' ! environment new_resource.environment! end! end! end! ! web_app 'awesome' do ! docroot '/srv/awesome/current/public' ! template 'awesome.conf.erb' ! server_name "awesome.#{node['domain']}" ! server_aliases [ 'awesome', node['hostname'] ] ! rails_env node.chef_environment! end +-- 2 lines: include_recipe 'mysql'----------------------------------------------------------------------------------------------------- ------------------! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'! ! group 'adm'! ! user 'spiffy' do group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "/srv/spiffy/shared/#{dir}" do owner 'spiffy' group 'adm'! mode '0750'! end! end! ! template '/srv/spiffy/shared/config/database.yml' do source 'database.yml.erb'! owner 'spiffy' group 'adm'! mode '0640'! variables(! database: {! host: 'spiffy-db.awsm.net', adapter: 'mysql2',! database: 'spiffy', username: 'spiffy', password: 's3kr3t'! },! rails_env: node.chef_environment! )! end! ! deploy '/srv/spiffy' do repo 'git@github.com/awsm/spiffy' user 'spiffy' group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user 'spiffy' environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user 'spiffy' environment new_resource.environment! end! end! end! ! web_app 'spiffy' do docroot '/srv/spiffy/current/public' template 'spiffy.conf.erb' server_name "spiffy.#{node['domain']}" server_aliases [ 'spiffy', node['hostname'] ] rails_env node.chef_environment! end
  34. Divergence

  35. Don't Repeat Yourself

  36. Don't Yo self wReck

  37. include_recipe 'mysql'! include_recipe 'apache2'! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'!

    ! group 'adm'! ! user 'awesome' do! group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "/srv/awesome/shared/#{dir}" do! owner 'awesome'! group 'adm'! mode '0750'! end! end! ! template '/srv/awesome/shared/config/database.yml' do! source 'database.yml.erb'! owner 'awesome'! group 'adm'! mode '0640'! variables(! database: {! host: 'awesome-db.awsm.net',! adapter: 'mysql2',! database: 'awesome',! username: 'awesome',! password: 's3kr3t'! },! rails_env: node.chef_environment! )! end! ! deploy '/srv/awesome' do! repo 'git@github.com/awsm/awesome'! user 'awesome'! group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user 'awesome'! environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user 'awesome'! environment new_resource.environment! end! end! end! ! web_app 'awesome' do! docroot '/srv/awesome/current/public'! template 'awesome.conf.erb'! server_name "awesome.#{node['domain']}"! server_aliases [ 'awesome', node['hostname'] ]! rails_env node.chef_environment! end include_recipe 'mysql'! include_recipe 'apache2'! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'! ! group 'adm'! ! user 'spiffy' do! group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "/srv/spiffy/shared/#{dir}" do! owner 'spiffy'! group 'adm'! mode '0750'! end! end! ! template '/srv/spiffy/shared/config/database.yml' do! source 'database.yml.erb'! owner 'spiffy'! group 'adm'! mode '0640'! variables(! database: {! host: 'spiffy-db.awsm.net',! adapter: 'mysql2',! database: 'spiffy',! username: 'spiffy',! password: 's3kr3t'! },! rails_env: node.chef_environment! )! end! ! deploy '/srv/spiffy' do! repo 'git@github.com/awsm/spiffy'! user 'spiffy'! group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user 'spiffy'! environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user 'spiffy'! environment new_resource.environment! end! end! end! ! web_app 'spiffy' do! docroot '/srv/spiffy/current/public'! template 'spiffy.conf.erb'! server_name "spiffy.#{node['domain']}"! server_aliases [ 'spiffy', node['hostname'] ]! rails_env node.chef_environment! end
  38. Def•i•ni•tion n. A statement conveying fundamental character.

  39. define :ruby_app_deploy, repo: nil do! params[:repo]! # ...! end

  40. params = {} ! params[:user] = 'spiffy' ! params[:deploy_to] =

    '/srv/spiffy' ! params[:database] = { ! host: 'spiffy-db.awsm.net', ! adapter: 'mysql2', ! database: 'spiffy', ! username: 'spiffy', ! password: 's3kr3t' ! } ! params[:repo] = 'git@github.com/awsm/spiffy' ! params[:app_name] = 'spiffy' ! include_recipe 'apache2'! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'! ! group 'adm'! ! user params[:user] do ! group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "#{params[:deploy_to]}/shared/#{dir}" do ! owner params[:user] ! group 'adm'! mode '0750'! end! end! ! template "#{params[:deploy_to]}/shared/config/database.yml" do ! source 'database.yml.erb'! owner params[:user] ! group 'adm'! mode '0640'! variables(! database: params[:database], ! rails_env: node.chef_environment! )! end! ! deploy params[:deploy_to] do ! repo params[:repo] ! user params[:user] ! group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user params[:user] ! environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user params[:user] ! environment new_resource.environment! end! end! end! ! web_app params[:app_name] do ! docroot "#{params[:deploy_to]}/current/public" ! template 'vhost.conf.erb' ! server_name "#{params[:app_name]}.#{node['domain']}" ! server_aliases [ params[:app_name], node['hostname'] ] ! rails_env node.chef_environment! end
  41. params = {} ! params[:user] = 'spiffy' ! params[:deploy_to] =

    '/srv/spiffy' ! params[:database] = { ! host: 'spiffy-db.awsm.net', ! adapter: 'mysql2', ! database: 'spiffy', ! username: 'spiffy', ! password: 's3kr3t' ! } ! params[:repo] = 'git@github.com/simply_spiffy/spiffy' ! params[:app_name] = 'spiffy' ! include_recipe 'apache2'! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'! ! group 'adm'! ! user params[:user] do ! group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "#{params[:deploy_to]}/shared/#{dir}" do ! owner params[:user] ! group 'adm'! mode '0750'! end! end! ! template "#{params[:deploy_to]}/shared/config/database.yml" do ! source 'database.yml.erb'! owner params[:user] ! group 'adm'! mode '0640'! variables(! database: params[:database], ! rails_env: node.chef_environment! )! end! ! deploy params[:deploy_to] do ! repo params[:repo] ! user params[:user] ! group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user params[:user] ! environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user params[:user] ! environment new_resource.environment! end! end! end! ! web_app params[:app_name] do ! docroot "#{params[:deploy_to]}/current/public" ! template 'vhost.conf.erb' ! server_name "#{params[:app_name]}.#{node['domain']}" ! server_aliases [ params[:app_name], node['hostname'] ] ! rails_env node.chef_environment! end params = {}! params[:user] = 'spiffy'! params[:deploy_to] = '/srv/spiffy'! params[:database] = {! host: 'spiffy-db.awsm.net',! adapter: 'mysql2',! database: 'spiffy',! username: 'spiffy',! password: 's3kr3t'! }! params[:repo] = 'git@github.com/awsm/spiffy'! params[:app_name] = 'spiffy'
  42. params = {} ! params[:user] = 'spiffy' ! params[:deploy_to] =

    '/srv/spiffy' ! params[:database] = { ! host: 'spiffy-db.awsm.net', ! adapter: 'mysql2', ! database: 'spiffy', ! username: 'spiffy', ! password: 's3kr3t' ! } ! params[:repo] = 'git@github.com/simply_spiffy/spiffy' ! params[:app_name] = 'spiffy' ! include_recipe 'apache2'! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'! ! group 'adm'! ! user params[:user] do ! group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "#{params[:deploy_to]}/shared/#{dir}" do ! owner params[:user] ! group 'adm'! mode '0750'! end! end! ! template "#{params[:deploy_to]}/shared/config/database.yml" do ! source 'database.yml.erb'! owner params[:user] ! group 'adm'! mode '0640'! variables(! database: params[:database], ! rails_env: node.chef_environment! )! end! ! deploy params[:deploy_to] do ! repo params[:repo] ! user params[:user] ! group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user params[:user] ! environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user params[:user] ! environment new_resource.environment! end! end! end! ! web_app params[:app_name] do ! docroot "#{params[:deploy_to]}/current/public" ! template 'vhost.conf.erb' ! server_name "#{params[:app_name]}.#{node['domain']}" ! server_aliases [ params[:app_name], node['hostname'] ] ! rails_env node.chef_environment! end define :ruby_app_deploy,! user: nil,! deploy_to: nil,! database: nil,! repo: nil do! ! include_recipe 'sqlite'! include_recipe 'apache2'! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'! ! group 'adm'! ! # ...! ! web_app params[:name] do! docroot "#{params[:deploy_to]}/current/public"! template 'vhost.conf.erb'! server_name "#{params[:name]}.#{node['domain']}"! server_aliases [ params[:name], node['hostname'] ]! rails_env node.chef_environment! end! end
  43. params = {}! params[:user] = 'spiffy'! params[:deploy_to] = '/srv/spiffy'! params[:database]

    = {! host: 'spiffy-db.awsm.net',! adapter: 'mysql2',! database: 'spiffy',! username: 'spiffy',! password: 's3kr3t'! }! params[:repo] = 'git@github.com/awsm/spiffy'! params[:app_name] = 'spiffy'! ! ruby_app_deploy params[:app_name] do! repo params[:repo]! database params[:database]! user params[:user]! deploy_to params[:deploy_to]! end params = {}! params[:user] = 'spiffy'! params[:deploy_to] = '/srv/spiffy'! params[:database] = {! host: 'spiffy-db.awsm.net',! adapter: 'mysql2',! database: 'spiffy',! username: 'spiffy',! password: 's3kr3t'! }! params[:repo] = 'git@github.com/simply_spiffy/spiffy'! params[:app_name] = 'spiffy'! ! include_recipe 'sqlite'! include_recipe 'apache2'! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'! ! group 'adm'! ! user params[:user] do! group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "#{params[:deploy_to]}/shared/#{dir}" do! owner params[:user]! group 'adm'! mode '0750'! end! end! ! template "#{params[:deploy_to]}/shared/config/database.yml" do! source 'database.yml.erb'! owner params[:user]! group 'adm'! mode '0640'! variables(! database: params[:database],! rails_env: node.chef_environment! )! end! ! deploy params[:deploy_to] do! repo params[:repo]! user params[:user]! group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user params[:user]! environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user params[:user]! environment new_resource.environment! end! end! end! ! web_app params[:app_name] do! docroot "#{params[:deploy_to]}/current/public"! template 'vhost.conf.erb'! server_name "#{params[:app_name]}.#{node['domain']}"! server_aliases [ params[:app_name], node['hostname'] ]! rails_env node.chef_environment! end! !
  44. ruby_app_deploy 'spiffy' do! repo 'git@github.com/awsm/spiffy'! database host: 'spiffy-db.awsm.net',! adapter: 'mysql2',!

    database: 'spiffy',! username: 'spiffy',! password: 's3kr3t'! user 'spiffy'! deploy_to '/srv/spiffy'! end
  45. ruby_app_deploy 'spiffy' do! repo 'git@github.com/awsm/spiffy'! database host: 'spiffy-db.awsm.net',! adapter: 'mysql2',!

    database: 'spiffy',! username: 'spiffy',! password: 's3kr3t'! user 'spiffy'! deploy_to '/srv/spiffy'! end include_recipe 'mysql'! include_recipe 'apache2'! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'! ! group 'adm'! ! user 'spiffy' do! group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "/srv/spiffy/shared/#{dir}" do! owner 'spiffy'! group 'adm'! mode '0750'! end! end! ! template '/srv/spiffy/shared/config/database.yml' do! source 'database.yml.erb'! owner 'spiffy'! group 'adm'! mode '0640'! variables(! database: {! host: 'spiffy-db.awsm.net',! adapter: 'mysql2',! database: 'spiffy',! username: 'spiffy',! password: 's3kr3t'! },! rails_env: node.chef_environment! )! end! ! deploy '/srv/spiffy' do! repo 'git@github.com/awsm/spiffy'! user 'spiffy'! group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user 'spiffy'! environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user 'spiffy'! environment new_resource.environment! end! end! end! ! web_app 'spiffy' do! docroot '/srv/spiffy/current/public'! template 'spiffy.conf.erb'! server_name "spiffy.#{node['domain']}"! server_aliases [ 'spiffy', node['hostname'] ]! rails_env node.chef_environment! end
  46. ruby_app_deploy 'spiffy' do! repo 'git@github.com/awsm/spiffy'! database host: 'spiffy-db.awsm.net',! adapter: 'mysql2',!

    database: 'spiffy',! username: 'spiffy',! password: 's3kr3t'! user 'spiffy'! deploy_to '/srv/spiffy'! end
  47. Pros copy-paste DRY Cons more files

  48. E.g. nfs_mount

  49. None
  50. Two apps Two services Two recipes

  51. Two apps Two services Two recipes Product-Market Fit!

  52. Three apps Three services Three recipes

  53. Something different

  54. template '/srv/fancy/etc/features.json' do! group 'wheel'! owner 'fancy'! mode '0664'! variables

    all_my_features! # notifies :reload, 'ruby_app_deploy[fancy]'! end! ! ruby_app_deploy 'fancy' do! repo 'git@github.com/awsm/fancy'! database host: 'fancy-db.awsm.net',! adapter: 'mysql2',! database: 'fancy',! username: 'fancy',! password: 's3kr3t'! user 'fancy'! deploy_to '/srv/fancy'! end
  55. Lightweight Resource/Provider LWRP

  56. re•source n. an asset that can be drawn on in

    order to function effectively
  57. # provider! ! action :create do! file "/tmp/#{new_resource.name}" do! owner

    new_resource.name! group new_resource.name! mode '0664'! action :create! end! end # resource! ! actions :create! ! attribute :name, :kind_of => String
  58. App deploy lwrp

  59. define :ruby_app_deploy,! user: nil,! deploy_to: nil,! database: nil,! repo: nil

    do! ! require 'ostruct' ! ! new_resource = OpenStruct.new ! new_resource.name = params[:name] ! new_resource.user = params[:user] ! new_resource.deploy_to = params[:deploy_to] ! new_resource.repo = params[:repo] ! new_resource.database = params[:database] ! ! include_recipe 'sqlite'! include_recipe 'apache2'! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'! ! group 'adm'! ! user new_resource.user do ! group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "#{new_resource.deploy_to}/shared/#{dir}" do ! owner new_resource.user ! group 'adm'! mode '0750'! end! end! ! template "#{new_resource.deploy_to}/shared/config/database.yml" do ! source 'database.yml.erb'! owner new_resource.user ! group 'adm'! mode '0640'! variables(! database: new_resource.database, ! rails_env: node.chef_environment! )! end! ! deploy new_resource.deploy_to do ! repo new_resource.repo ! user new_resource.user ! group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user new_resource.user ! environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user new_resource.user ! environment new_resource.environment! end! end! end! ! web_app new_resource.name do ! docroot "#{new_resource.deploy_to}/current/public" ! template 'vhost.conf.erb'! server_name "#{new_resource.name}.#{node['domain']}" ! server_aliases [ new_resource.name, node['hostname'] ] ! rails_env node.chef_environment! end! end! require 'ostruct'! ! new_resource = OpenStruct.new! new_resource.name = params[:name]! new_resource.user = params[:user]! new_resource.deploy_to = params[:deploy_to]! new_resource.repo = params[:repo]! new_resource.database = params[:database]
  60. # ruby_app_deploy resource! ! action :create! ! attribute :name, :kind_of

    => String! attribute :user, :kind_of => String! attribute :deploy_to, :kind_of => String! attribute :repo, :kind_of => String! attribute :database, :kind_of => Hash # ruby_app_deploy definition! ! require 'ostruct'! new_resource = OpenStruct.new! ! new_resource.name = params[:name]! new_resource.user = params[:user]! new_resource.deploy_to = params[:deploy_to]! new_resource.repo = params[:repo]! new_resource.database = params[:database]
  61. define :ruby_app_deploy,! user: nil,! deploy_to: nil,! database: nil,! repo: nil

    do! ! require 'ostruct' ! ! new_resource = OpenStruct.new ! new_resource.name = params[:name] ! new_resource.user = params[:user] ! new_resource.deploy_to = params[:deploy_to] ! new_resource.repo = params[:repo] ! new_resource.database = params[:database] ! ! include_recipe 'sqlite'! include_recipe 'apache2'! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'! ! group 'adm'! ! user new_resource.user do ! group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "#{new_resource.deploy_to}/shared/#{dir}" do ! owner new_resource.user ! group 'adm'! mode '0750'! end! end! ! template "#{new_resource.deploy_to}/shared/config/database.yml" do ! source 'database.yml.erb'! owner new_resource.user ! group 'adm'! mode '0640'! variables(! database: new_resource.database, ! rails_env: node.chef_environment! )! end! ! deploy new_resource.deploy_to do ! repo new_resource.repo ! user new_resource.user ! group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user new_resource.user ! environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user new_resource.user ! environment new_resource.environment! end! end! end! ! web_app new_resource.name do ! docroot "#{new_resource.deploy_to}/current/public" ! template 'vhost.conf.erb'! server_name "#{new_resource.name}.#{node['domain']}" ! server_aliases [ new_resource.name, node['hostname'] ] ! rails_env node.chef_environment! end! end! # ruby_app_deploy provider! ! action :create do! include_recipe 'sqlite'! include_recipe 'apache2'! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'! ! group 'adm'! ! user new_resource.user do! group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "#{new_resource.deploy_to}/shared/#{dir}" do! owner new_resource.user! group 'adm'! mode '0750'! end! end! ! template "#{new_resource.deploy_to}/shared/config/database.yml" do! source 'database.yml.erb'! owner new_resource.user! group 'adm'! mode '0640'! variables(! database: new_resource.database,! rails_env: node.chef_environment! )! end! ! deploy new_resource.deploy_to do! repo new_resource.repo! user new_resource.user! group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user new_resource.user! environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user new_resource.user! environment new_resource.environment! end! end! end! ! web_app new_resource.name do! docroot "#{new_resource.deploy_to}/current/public"! template 'vhost.conf.erb'! server_name "#{new_resource.name}.#{node['domain']}"! server_aliases [ new_resource.name, node['hostname'] ]! rails_env node.chef_environment! end! end
  62. # ruby_app_deploy resource! ! action :create! ! attribute :name, :kind_of

    => String! attribute :user, :kind_of => String! attribute :deploy_to, :kind_of => String! attribute :repo, :kind_of => String! attribute :database, :kind_of => Hash! # ruby_app_deploy provider! ! action :create do! include_recipe 'sqlite'! include_recipe 'apache2'! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'! ! group 'adm'! ! user new_resource.user do! group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "#{new_resource.deploy_to}/shared/#{dir}" do! owner new_resource.user! group 'adm'! mode '0750'! end! end! ! template "#{new_resource.deploy_to}/shared/config/database.yml" do! source 'database.yml.erb'! owner new_resource.user! group 'adm'! mode '0640'! variables(! database: new_resource.database,! rails_env: node.chef_environment! )! end! ! deploy new_resource.deploy_to do! repo new_resource.repo! user new_resource.user! group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user new_resource.user! environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user new_resource.user! environment new_resource.environment! end! end! end! ! web_app new_resource.name do! docroot "#{new_resource.deploy_to}/current/public"! template 'vhost.conf.erb'! server_name "#{new_resource.name}.#{node['domain']}"! server_aliases [ new_resource.name, node['hostname'] ]! rails_env node.chef_environment! end! end
  63. Pros notifications validation error handling actions Cons even more files

    diff syntax
  64. E.g. user, vhost

  65. None
  66. Three apps Three services Three recipes

  67. Three apps Three services Three recipes Moar! Moar! Moar!

  68. Five apps Three stacks

  69. java vs ruby stacks

  70. Cross cutting concerns diff implementations

  71. HWRP

  72. heav•y•weight n. a leader in one’s field

  73. class Chef! class Resource::Minimal < Resource! def initialize(name, run_context =

    nil)! super! @app_name = name! @action.push(:create)! @allowed_actions.push(:create)! end! ! def app_name(arg=nil)! set_or_return(! :app_name,! arg,! :kind_of => [ String ]! )! end! end! ! class Provider::Minimal < Provider! def load_current_resource! end! ! def action_create! file "/tmp/#{new_resource.app_name}" do! owner new_resource.app_name! group new_resource.app_name! mode '0664'! action :create! end! end! end! end
  74. class Chef! class Resource::RubyAppDeploy < Resource! def initialize(name, run_context =

    nil)! super! @app_name = name! @action.push(:create)! @allowed_actions.push(:create,:reload)! end! ! def user(arg=nil)! set_or_return(! :user,! arg,! :kind_of => [ String ]! )! end! def deploy_to(arg=nil)! set_or_return(! :deploy_to,! arg,! :kind_of => [ String ]! )! end! def repo(arg=nil)! set_or_return(! :repo,! arg,! :kind_of => [ String ]! )! end! def database(arg=nil)! set_or_return(! :database,! arg,! :kind_of => [ String ]! )! end! end! end
  75. def initialize(name, run_context = nil)! super! @app_name = name! @action.push(:create)!

    @allowed_actions.push(:create,:reload)! end
  76. def user(arg=nil)! set_or_return(! :user,! arg,! :kind_of => [ String ]!

    )! end
  77. class Chef! class Provider::RubyAppDeploy < Provider! def load_current_resource! @current_resource =

    Chef::Resource::RubyAppDeploy.new(@new_resource.name)! @current_resource! end! ! def action_create! include_recipe 'sqlite'! include_recipe 'apache2'! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'! ! group 'adm'! ! user new_resource.user do! group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "#{new_resource.deploy_to}/shared/#{dir}" do! owner new_resource.user! group 'adm'! mode '0750'! end! end! ! template "#{new_resource.deploy_to}/shared/config/database.yml" do! source 'database.yml.erb'! owner new_resource.user! group 'adm'! mode '0640'! variables(! database: new_resource.database,! rails_env: node.chef_environment! )! end! ! deploy new_resource.deploy_to do! repo new_resource.repo! user new_resource.user! group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user new_resource.user! environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user new_resource.user! environment new_resource.environment! end! end! end! ! web_app new_resource.name do! docroot "#{new_resource.deploy_to}/current/public"! template 'vhost.conf.erb'! server_name "#{new_resource.name}.#{node['domain']}"! server_aliases [ new_resource.name, node['hostname'] ]! rails_env node.chef_environment! end! end! end! end
  78. def load_current_resource! @current_resource = Chef::Resource::RubyAppDeploy.new(@new_resource.name)! @current_resource! end

  79. def action_create! # same as action :create! end

  80. class Chef! class Provider::RubyAppDeploy < Provider! def load_current_resource! @current_resource =

    Chef::Resource::RubyAppDeploy.new(@new_resource.name)! @current_resource! end! ! def action_create! include_recipe 'sqlite'! include_recipe 'apache2'! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'! ! group 'adm'! ! user new_resource.user do! group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "#{new_resource.deploy_to}/shared/#{dir}" do! owner new_resource.user! group 'adm'! mode '0750'! end! end! ! template "#{new_resource.deploy_to}/shared/config/database.yml" do! source 'database.yml.erb'! owner new_resource.user! group 'adm'! mode '0640'! variables(! database: new_resource.database,! rails_env: node.chef_environment! )! end! ! deploy new_resource.deploy_to do! repo new_resource.repo! user new_resource.user! group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user new_resource.user! environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user new_resource.user! environment new_resource.environment! end! end! end! ! web_app new_resource.name do! docroot "#{new_resource.deploy_to}/current/public"! template 'vhost.conf.erb'! server_name "#{new_resource.name}.#{node['domain']}"! server_aliases [ new_resource.name, node['hostname'] ]! rails_env node.chef_environment! end! end! end! end class Chef! class Resource::RubyAppDeploy < Resource! def initialize(name, run_context = nil)! super! @app_name = name! @action.push(:create)! @allowed_actions.push(:create,:reload)! end! ! def user(arg=nil)! set_or_return(! :user,! arg,! :kind_of => [ String ]! )! end! def deploy_to(arg=nil)! set_or_return(! :deploy_to,! arg,! :kind_of => [ String ]! )! end! def repo(arg=nil)! set_or_return(! :repo,! arg,! :kind_of => [ String ]! )! end! def database(arg=nil)! set_or_return(! :database,! arg,! :kind_of => [ String ]! )! end! end! end
  81. class Chef! class Provider::RubyAppDeploy < Provider! def load_current_resource! @current_resource =

    Chef::Resource::RubyAppDeploy.new(@new_resource.name)! @current_resource! end! ! def action_create! include_recipe 'sqlite'! include_recipe 'apache2'! include_recipe 'apache2::mod_rewrite'! include_recipe 'passenger_apache2::mod_rails'! include_recipe 'ruby'! ! group 'adm'! ! user new_resource.user do! group 'adm'! end! ! gem_package 'bundler'! ! %w{log vendor_bundle config}.each do |dir|! directory "#{new_resource.deploy_to}/shared/#{dir}" do! owner new_resource.user! group 'adm'! mode '0750'! end! end! ! template "#{new_resource.deploy_to}/shared/config/database.yml" do! source 'database.yml.erb'! owner new_resource.user! group 'adm'! mode '0640'! variables(! database: new_resource.database,! rails_env: node.chef_environment! )! end! ! deploy new_resource.deploy_to do! repo new_resource.repo! user new_resource.user! group 'adm'! migrate true! migration_command 'bundle exec rake db:migrate'! environment 'RAILS_ENV' => node.chef_environment! create_dirs_before_symlink %w{tmp public config vendor}! symlinks 'log' => 'log',! 'vendor_bundle' => 'vendor/bundle'! restart_command 'touch tmp/restart.txt'! ! before_migrate do! execute 'bundle install --deployment' do! cwd release_path! user new_resource.user! environment new_resource.environment! end! end! before_symlink do! execute 'bundle exec rake assets:precompile' do! cwd release_path! user new_resource.user! environment new_resource.environment! end! end! end! ! web_app new_resource.name do! docroot "#{new_resource.deploy_to}/current/public"! template 'vhost.conf.erb'! server_name "#{new_resource.name}.#{node['domain']}"! server_aliases [ new_resource.name, node['hostname'] ]! rails_env node.chef_environment! end! end! end! end class Chef! class Resource::RubyAppDeploy < Resource! def initialize(name, run_context = nil)! super! @app_name = name! @action.push(:create)! @allowed_actions.push(:create,:reload)! end! ! def user(arg=nil)! set_or_return(! :user,! arg,! :kind_of => [ String ]! )! end! def deploy_to(arg=nil)! set_or_return(! :deploy_to,! arg,! :kind_of => [ String ]! )! end! def repo(arg=nil)! set_or_return(! :repo,! arg,! :kind_of => [ String ]! )! end! def database(arg=nil)! set_or_return(! :database,! arg,! :kind_of => [ String ]! )! end! end! end
  82. Pros pure ruby subclassing multi-provider Cons pure ruby

  83. E.g. service, package, plugin

  84. c.f. poise

  85. Only you know what problems are critical to your business

  86. Only you know what solutions are critical to your business

  87. Only you know what ideas are critical to your business

  88. tl;dr

  89. You can write your infrastructure as code

  90. Someone has understand your infrastructure

  91. Build that knowledge into a language

  92. Build that language into your code

  93. We can build it; we have the technology

  94. Definitions LWRPs HWRPs

  95. Be not afraid

  96. Build a higher-level language, Find higher-level ideas, Solve higher-level problems,

    and repeat.
  97. None
  98. None