From 35df416a00c272c86191a371431dfefdf9720aba Mon Sep 17 00:00:00 2001 From: Robert R Evans Date: Fri, 11 Feb 2011 09:29:45 -0800 Subject: [PATCH] First commit --- .gitignore | 2 + LICENSE | 201 + NOTICE | 15 + Rakefile | 46 + activemq/README.rdoc | 39 + activemq/attributes/activemq.rb | 21 + activemq/metadata.json | 51 + activemq/metadata.rb | 14 + activemq/recipes/default.rb | 42 + .../templates/default/sv-activemq-log-run.erb | 2 + .../templates/default/sv-activemq-run.erb | 3 + apache2/README.rdoc | 78 + apache2/attributes/apache.rb | 77 + apache2/definitions/apache_conf.rb | 25 + apache2/definitions/apache_module.rb | 43 + apache2/definitions/apache_site.rb | 40 + apache2/definitions/web_app.rb | 49 + .../default/apache2_module_conf_generate.pl | 41 + apache2/metadata.json | 523 + apache2/metadata.rb | 197 + apache2/recipes/default.rb | 191 + apache2/recipes/god_monitor.rb | 33 + apache2/recipes/mod_alias.rb | 22 + apache2/recipes/mod_auth_basic.rb | 20 + apache2/recipes/mod_auth_digest.rb | 20 + apache2/recipes/mod_auth_openid.rb | 59 + apache2/recipes/mod_authn_file.rb | 20 + apache2/recipes/mod_authnz_ldap.rb | 20 + apache2/recipes/mod_authz_default.rb | 20 + apache2/recipes/mod_authz_groupfile.rb | 20 + apache2/recipes/mod_authz_host.rb | 20 + apache2/recipes/mod_authz_user.rb | 20 + apache2/recipes/mod_autoindex.rb | 22 + apache2/recipes/mod_cgi.rb | 20 + apache2/recipes/mod_dav.rb | 20 + apache2/recipes/mod_dav_svn.rb | 22 + apache2/recipes/mod_deflate.rb | 22 + apache2/recipes/mod_dir.rb | 22 + apache2/recipes/mod_env.rb | 20 + apache2/recipes/mod_expires.rb | 20 + apache2/recipes/mod_fcgid.rb | 46 + apache2/recipes/mod_headers.rb | 20 + apache2/recipes/mod_ldap.rb | 20 + apache2/recipes/mod_log_config.rb | 24 + apache2/recipes/mod_mime.rb | 22 + apache2/recipes/mod_negotiation.rb | 22 + apache2/recipes/mod_php5.rb | 32 + apache2/recipes/mod_proxy.rb | 22 + apache2/recipes/mod_proxy_ajp.rb | 20 + apache2/recipes/mod_proxy_balancer.rb | 20 + apache2/recipes/mod_proxy_connect.rb | 20 + apache2/recipes/mod_proxy_http.rb | 20 + apache2/recipes/mod_python.rb | 22 + apache2/recipes/mod_rewrite.rb | 20 + apache2/recipes/mod_setenvif.rb | 22 + apache2/recipes/mod_ssl.rb | 42 + apache2/recipes/mod_status.rb | 22 + apache2/templates/default/a2dismod.erb | 22 + apache2/templates/default/a2dissite.erb | 29 + apache2/templates/default/a2enmod.erb | 37 + apache2/templates/default/a2ensite.erb | 38 + apache2/templates/default/apache2.conf.erb | 230 + apache2/templates/default/apache2.god.erb | 19 + apache2/templates/default/charset.erb | 6 + apache2/templates/default/default-site.erb | 57 + .../templates/default/mod_auth_openid.rb.erb | 12 + apache2/templates/default/mods/README | 2 + apache2/templates/default/mods/alias.conf.erb | 24 + .../default/mods/authopenid.load.erb | 1 + .../templates/default/mods/autoindex.conf.erb | 101 + .../templates/default/mods/deflate.conf.erb | 16 + apache2/templates/default/mods/dir.conf.erb | 5 + apache2/templates/default/mods/fcgid.conf.erb | 10 + apache2/templates/default/mods/mime.conf.erb | 191 + .../default/mods/negotiation.conf.erb | 18 + apache2/templates/default/mods/proxy.conf.erb | 19 + .../templates/default/mods/setenvif.conf.erb | 28 + apache2/templates/default/mods/ssl.conf.erb | 72 + .../templates/default/mods/status.conf.erb | 16 + apache2/templates/default/port_apache.erb | 2 + apache2/templates/default/ports.conf.erb | 6 + apache2/templates/default/security.erb | 50 + apache2/templates/default/web_app.conf.erb | 43 + apparmor/metadata.json | 40 + apparmor/metadata.rb | 6 + apparmor/recipes/default.rb | 26 + apt/files/default/apt-cacher | 9 + apt/files/default/apt-cacher.conf | 144 + apt/files/default/apt-proxy-v2.conf | 50 + apt/metadata.json | 51 + apt/metadata.rb | 11 + apt/recipes/cacher.rb | 42 + apt/recipes/default.rb | 33 + apt/recipes/proxy.rb | 34 + aws/libraries/ec2.rb | 56 + aws/providers/ebs_volume.rb | 222 + aws/providers/elastic_ip.rb | 83 + aws/recipes/default.rb | 27 + aws/resources/ebs_volume.rb | 11 + aws/resources/elastic_ip.rb | 6 + bluepill/attributes/bluepill.rb | 4 + bluepill/definitions/bluepill_monitor.rb | 21 + bluepill/metadata.json | 47 + bluepill/metadata.rb | 5 + bluepill/recipes/default.rb | 27 + bluepill/templates/default/init.sh.erb | 35 + boost/README.rdoc | 33 + boost/metadata.json | 40 + boost/metadata.rb | 8 + boost/recipes/default.rb | 19 + bootstrap/README.rdoc | 165 + bootstrap/attributes/bootstrap.rb | 50 + bootstrap/metadata.json | 77 + bootstrap/metadata.rb | 14 + bootstrap/recipes/client.rb | 87 + bootstrap/recipes/default.rb | 18 + bootstrap/recipes/server.rb | 120 + bootstrap/templates/default/client.rb.erb | 30 + bootstrap/templates/default/server.rb.erb | 34 + .../default/sv-chef-client-log-run.erb | 2 + .../templates/default/sv-chef-client-run.erb | 4 + .../default/sv-chef-indexer-log-run.erb | 2 + .../templates/default/sv-chef-indexer-run.erb | 4 + .../default/sv-chef-server-log-run.erb | 2 + .../templates/default/sv-chef-server-run.erb | 4 + build-essential/metadata.json | 46 + build-essential/metadata.rb | 9 + build-essential/recipes/default.rb | 43 + build/attributes/build.rb | 2 + build/metadata.json | 38 + build/metadata.rb | 4 + build/recipes/default.rb | 41 + capistrano/definitions/cap_setup.rb | 46 + capistrano/metadata.json | 59 + capistrano/metadata.rb | 9 + capistrano/recipes/default.rb | 20 + chef/README.rdoc | 221 + chef/attributes/chef.rb | 61 + chef/attributes/server_proxy.rb | 23 + chef/metadata.json | 276 + chef/metadata.rb | 87 + chef/recipes/client.rb | 64 + chef/recipes/default.rb | 0 chef/recipes/server.rb | 75 + chef/recipes/server_proxy.rb | 61 + chef/templates/default/chef_server.conf.erb | 65 + chef/templates/default/client.rb.erb | 30 + chef/templates/default/server.rb.erb | 34 + collectd/attributes/collectd.rb | 22 + collectd/libraries/default.rb | 6 + collectd/metadata.json | 38 + collectd/metadata.rb | 4 + collectd/recipes/default.rb | 16 + collectd/templates/default/collectd.conf.erb | 40 + .../templates/default/collection.conf.erb | 3 + .../templates/default/thresholds.conf.erb | 38 + couchdb/metadata.json | 58 + couchdb/metadata.rb | 21 + couchdb/recipes/default.rb | 47 + couchdb/templates/default/port_couchdb.erb | 2 + cron/attributes/cron.rb | 1 + cron/metadata.json | 38 + cron/metadata.rb | 4 + cron/recipes/default.rb | 9 + ddclient/metadata.json | 38 + ddclient/metadata.rb | 4 + ddclient/recipes/default.rb | 24 + ddclient/templates/default/ddclient.conf.erb | 7 + .../templates/default/ddclient.default.erb | 2 + django/README.rdoc | 42 + django/metadata.json | 48 + django/metadata.rb | 14 + django/recipes/default.rb | 29 + djbdns/attributes/djbdns.rb | 45 + djbdns/metadata.json | 149 + djbdns/metadata.rb | 55 + djbdns/recipes/axfr.rb | 40 + djbdns/recipes/cache.rb | 43 + djbdns/recipes/default.rb | 101 + djbdns/recipes/internal_server.rb | 38 + djbdns/recipes/server.rb | 38 + djbdns/templates/default/dnscache-servers.erb | 1 + .../templates/default/sv-axfrdns-log-run.erb | 2 + djbdns/templates/default/sv-axfrdns-run.erb | 4 + .../default/sv-public-dnscache-log-run.erb | 2 + .../default/sv-public-dnscache-run.erb | 6 + .../default/sv-tinydns-internal-log-run.erb | 2 + .../default/sv-tinydns-internal-run.erb | 3 + .../templates/default/sv-tinydns-log-run.erb | 2 + djbdns/templates/default/sv-tinydns-run.erb | 3 + djbdns/templates/default/tinydns-data.erb | 4 + .../default/tinydns-internal-data.erb | 5 + dns/metadata.json | 42 + dns/metadata.rb | 4 + dns/recipes/client.rb | 7 + dns/recipes/default.rb | 0 dns/templates/default/resolv.conf.erb | 4 + drbd/metadata.json | 45 + drbd/metadata.rb | 10 + drbd/recipes/default.rb | 35 + dynomite/README.rdoc | 52 + dynomite/attributes/dynomite.rb | 31 + dynomite/metadata.json | 51 + dynomite/metadata.rb | 12 + dynomite/recipes/default.rb | 58 + .../templates/default/sv-dynomite-log-run.erb | 2 + .../templates/default/sv-dynomite-run.erb | 9 + ec2/attributes/ec2_recipe_options.rb | 28 + ec2/metadata.rb | 13 + ec2/recipes/default.rb | 19 + ejabberd/metadata.json | 38 + ejabberd/metadata.rb | 4 + ejabberd/recipes/default.rb | 20 + ejabberd/templates/default/ejabberd.cfg.erb | 197 + emacs/metadata.json | 49 + emacs/metadata.rb | 9 + emacs/recipes/default.rb | 21 + erlang/attributes/erlang.rb | 1 + erlang/metadata.json | 43 + erlang/metadata.rb | 9 + erlang/recipes/default.rb | 29 + fail2ban/metadata.json | 43 + fail2ban/metadata.rb | 9 + fail2ban/recipes/default.rb | 35 + fail2ban/templates/default/fail2ban.conf.erb | 34 + fail2ban/templates/default/jail.conf.erb | 202 + ganglia/attributes/ganglia.rb | 5 + ganglia/metadata.rb | 5 + ganglia/recipes/client.rb | 22 + ganglia/recipes/default.rb | 8 + ganglia/recipes/server.rb | 38 + .../templates/default/ganglia-vhost.conf.erb | 6 + ganglia/templates/default/gmetad.conf.erb | 117 + ganglia/templates/default/gmond.conf.erb | 336 + gems/README.rdoc | 31 + gems/attributes/gems.rb | 1 + gems/metadata.json | 40 + gems/metadata.rb | 7 + gems/recipes/default.rb | 29 + git/README.rdoc | 37 + git/metadata.json | 52 + git/metadata.rb | 15 + git/recipes/default.rb | 26 + git/recipes/server.rb | 28 + .../default/sv-git-daemon-log-run.erb | 2 + git/templates/default/sv-git-daemon-run.erb | 3 + gitosis/files/default/access.py | 102 + gitosis/files/default/serve.py | 207 + gitosis/metadata.json | 38 + gitosis/metadata.rb | 4 + gitosis/recipes/default.rb | 20 + gitweb/attributes/gitweb.rb | 1 + gitweb/metadata.json | 43 + gitweb/metadata.rb | 6 + gitweb/recipes/default.rb | 18 + .../templates/default/apache-vhost.conf.erb | 32 + gitweb/templates/default/apache.conf.erb | 32 + gitweb/templates/default/gitweb.conf.erb | 26 + gitweb/templates/default/projects.conf.erb | 4 + glassfish/README.rdoc | 8 + glassfish/attributes/glassfish.rb | 82 + glassfish/metadata.json | 50 + glassfish/metadata.rb | 11 + glassfish/recipes/default.rb | 73 + glassfish/templates/default/answer_file.erb | 27 + .../default/glassfish-init.d-script.erb | 44 + gnupg/metadata.json | 38 + gnupg/metadata.rb | 4 + gnupg/recipes/default.rb | 1 + god/README.rdoc | 49 + god/definitions/god_monitor.rb | 38 + god/metadata.json | 48 + god/metadata.rb | 14 + god/recipes/default.rb | 41 + god/templates/default/master.god.erb | 20 + god/templates/default/mongrel.god.erb | 40 + god/templates/default/sv-god-log-run.erb | 2 + god/templates/default/sv-god-run.erb | 6 + hadoop/README.rdoc | 63 + hadoop/metadata.json | 61 + hadoop/metadata.rb | 11 + hadoop/recipes/conf_pseudo.rb | 29 + hadoop/recipes/default.rb | 38 + hadoop/recipes/doc.rb | 22 + hadoop/recipes/hive.rb | 22 + hadoop/recipes/pig.rb | 22 + hadoop/templates/default/cloudera.list.erb | 2 + haproxy/README.rdoc | 37 + haproxy/metadata.json | 43 + haproxy/metadata.rb | 10 + haproxy/recipes/default.rb | 42 + haproxy/templates/default/haproxy-default.erb | 4 + haproxy/templates/default/haproxy.cfg.erb | 27 + heartbeat/metadata.json | 45 + heartbeat/metadata.rb | 10 + heartbeat/recipes/default.rb | 39 + hosts/attributes/hosts.rb | 1 + hosts/metadata.json | 38 + hosts/metadata.rb | 4 + hosts/recipes/default.rb | 6 + hosts/templates/default/hosts.erb | 27 + imagemagick/README.rdoc | 37 + imagemagick/metadata.json | 56 + imagemagick/metadata.rb | 10 + imagemagick/recipes/default.rb | 25 + imagemagick/recipes/rmagick.rb | 28 + instiki/README.rdoc | 33 + instiki/metadata.rb | 10 + instiki/recipes/default.rb | 50 + instiki/templates/default/instiki.conf.erb | 20 + integrity/attributes/integrity.rb | 11 + integrity/files/default/integrity_build.rb | 22 + integrity/metadata.json | 40 + integrity/metadata.rb | 5 + integrity/recipes/default.rb | 111 + integrity/templates/default/config.ru.erb | 14 + integrity/templates/default/config.yml.erb | 41 + integrity/templates/default/vhost.conf.erb | 12 + iptables/definitions/iptables_rule.rb | 34 + iptables/files/default/rebuild-iptables | 282 + iptables/metadata.json | 49 + iptables/metadata.rb | 9 + iptables/recipes/default.rb | 36 + iptables/templates/default/restrictive.erb | 14 + java/README.rdoc | 29 + java/files/default/java.seed | 11 + java/metadata.json | 43 + java/metadata.rb | 10 + java/recipes/default.rb | 45 + jira/attributes/jira.rb | 29 + jira/files/default/catalina.sh | 323 + jira/files/default/startup.sh | 48 + jira/metadata.json | 161 + jira/metadata.rb | 64 + jira/recipes/default.rb | 99 + jira/templates/default/apache.conf.erb | 32 + jira/templates/default/entityengine.xml.erb | 114 + jira/templates/default/server.xml.erb | 90 + jira/templates/default/sv-jira-log-run.erb | 3 + jira/templates/default/sv-jira-run.erb | 5 + keepalived/README.rdoc | 33 + keepalived/metadata.json | 40 + keepalived/metadata.rb | 7 + keepalived/recipes/default.rb | 35 + .../templates/default/keepalived.conf.erb | 48 + kickstart/README.rdoc | 51 + kickstart/attributes/kickstart.rb | 2 + kickstart/metadata.json | 49 + kickstart/metadata.rb | 9 + kickstart/recipes/default.rb | 18 + kickstart/recipes/server.rb | 52 + .../templates/default/kickstart.conf.erb | 27 + kickstart/templates/default/ks.cfg.erb | 250 + kvm/files/default/kvmtool | 54 + .../modules/2.6.27-11-server/kvm-amd.ko | Bin 0 -> 74568 bytes .../modules/2.6.27-11-server/kvm-intel.ko | Bin 0 -> 92051 bytes .../default/modules/2.6.27-11-server/kvm.ko | Bin 0 -> 285317 bytes .../default/templates/files/event.d/ttyS0 | 16 + .../default/templates/files/manifest.txt | 1 + .../default/templates/scripts/add-172-net.sh | 16 + .../default/templates/scripts/grub-console.sh | 13 + .../default/templates/scripts/postinstall.sh | 10 + .../templates/scripts/rename-fstab-devices.sh | 5 + .../default/templates/scripts/vmbuild.sh | 34 + .../templates/libvirt/libvirtxml.tmpl | 53 + kvm/metadata.json | 38 + kvm/metadata.rb | 4 + kvm/recipes/default.rb | 106 + kvm/templates/default/libvirtd.conf.erb | 227 + logrotate/metadata.json | 49 + logrotate/metadata.rb | 9 + logrotate/recipes/default.rb | 22 + logwatch/README.rdoc | 8 + logwatch/metadata.json | 51 + logwatch/metadata.rb | 12 + logwatch/recipes/default.rb | 23 + lvm/metadata.json | 49 + lvm/metadata.rb | 9 + lvm/recipes/default.rb | 21 + maatkit/files/default/maatkit-5014.tar.gz | Bin 0 -> 822002 bytes maatkit/files/default/mk-query-digest | 10308 ++++++++++++++++ maatkit/recipes/default.rb | 6 + man/metadata.json | 43 + man/metadata.rb | 9 + man/recipes/default.rb | 23 + maradns/attributes/maradns.rb | 1 + maradns/metadata.json | 63 + maradns/metadata.rb | 19 + maradns/recipes/default.rb | 52 + maradns/templates/default/mararc.erb | 31 + memcached/README.rdoc | 51 + memcached/attributes/memcached.rb | 3 + memcached/definitions/memcached_instance.rb | 30 + memcached/metadata.json | 77 + memcached/metadata.rb | 24 + memcached/recipes/default.rb | 55 + .../templates/default/memcached.conf.erb | 50 + .../templates/default/memcached.default.erb | 2 + .../default/sv-memcached-log-run.erb | 2 + .../templates/default/sv-memcached-run.erb | 3 + mercurial/metadata.json | 43 + mercurial/metadata.rb | 9 + mercurial/recipes/default.rb | 21 + mogilefs/attributes/mogilefs.rb | 17 + mogilefs/metadata.json | 48 + mogilefs/metadata.rb | 6 + mogilefs/recipes/default.rb | 33 + mogilefs/recipes/storenode.rb | 16 + mogilefs/recipes/tracker.rb | 12 + .../default/bluepill_mogilefsd.conf.erb | 16 + .../default/bluepill_mogstored.conf.erb | 16 + mogilefs/templates/default/mogilefs.conf.erb | 1 + mogilefs/templates/default/mogilefs.sh.erb | 1 + mogilefs/templates/default/mogilefsd.conf.erb | 9 + mogilefs/templates/default/mogstored.conf.erb | 3 + .../default/sv-mogilefsd-log-run.erb | 2 + .../templates/default/sv-mogilefsd-run.erb | 3 + .../default/sv-mogstored-log-run.erb | 2 + .../templates/default/sv-mogstored-run.erb | 3 + monit/libraries/monitrc.rb | 16 + monit/metadata.json | 38 + monit/metadata.rb | 5 + monit/recipes/default.rb | 21 + monit/templates/default/monitrc.erb | 14 + munin/metadata.json | 43 + munin/metadata.rb | 9 + munin/recipes/default.rb | 37 + munin/templates/default/munin-node.conf.erb | 41 + munin/templates/default/port_munin.erb | 2 + mysql/README.rdoc | 95 + mysql/attributes/server.rb | 42 + mysql/libraries/database.rb | 15 + mysql/metadata.json | 184 + mysql/metadata.rb | 73 + mysql/providers/database.rb | 13 + mysql/recipes/client.rb | 50 + mysql/recipes/default.rb | 20 + mysql/recipes/server.rb | 88 + mysql/recipes/server_ec2.rb | 49 + mysql/resources/database.rb | 5 + mysql/templates/centos/my.cnf.erb | 12 + mysql/templates/default/debian.cnf.erb | 11 + mysql/templates/default/grants.sql.erb | 13 + mysql/templates/default/my.cnf.erb | 163 + mysql/templates/default/mysql-server.seed.erb | 10 + mysql/templates/default/port_mysql.erb | 3 + mysql/templates/redhat/my.cnf.erb | 12 + mysql/templates/ubuntu-8.04/my.cnf.erb | 156 + mysql/templates/ubuntu-9.10/my.cnf.erb | 158 + nagios/attributes/client.rb | 10 + nagios/attributes/server.rb | 30 + nagios/definitions/nagios_conf.rb | 18 + nagios/definitions/nagios_service_template.rb | 3 + nagios/files/default/notifiers/campfire | 68 + nagios/files/default/notifiers/clickatell | 58 + nagios/files/default/notifiers/jabber | 40 + nagios/files/default/notifiers/jabber_bot | 51 + .../default/notifiers/send_passive_check | 19 + .../default/plugins/check-mysql-slave.pl | 158 + nagios/files/default/plugins/check_mem.sh | 98 + .../default/plugins/check_mysqlhealth.pl | 159 + nagios/files/default/plugins/check_netapp.pl | 440 + nagios/files/default/plugins/check_solr.rb | 117 + nagios/files/default/plugins/db_queues.rb | 102 + nagios/files/default/plugins/file_queues.rb | 84 + nagios/files/default/plugins/haproxy_stats.rb | 88 + nagios/files/default/plugins/nagios_helper.rb | 26 + nagios/libraries/default.rb | 19 + nagios/metadata.json | 55 + nagios/metadata.rb | 6 + nagios/recipes/checks.rb | 1 + nagios/recipes/client.rb | 30 + nagios/recipes/default.rb | 0 nagios/recipes/server.rb | 154 + nagios/templates/default/apache2.conf.erb | 29 + nagios/templates/default/commands.cfg.erb | 324 + nagios/templates/default/contacts.cfg.erb | 60 + nagios/templates/default/hostgroups.cfg.erb | 16 + nagios/templates/default/hosts.cfg.erb | 11 + nagios/templates/default/nagios.cfg.erb | 151 + nagios/templates/default/nrpe.cfg.erb | 19 + nagios/templates/default/services.cfg.erb | 30 + .../default/sv-nagios-bot-log-run.erb | 2 + .../templates/default/sv-nagios-bot-run.erb | 4 + .../templates/default/sv-nagios3-log-run.erb | 2 + nagios/templates/default/sv-nagios3-run.erb | 4 + nagios/templates/default/templates.cfg.erb | 109 + nagios/templates/default/timeperiods.cfg.erb | 14 + nanite/metadata.json | 52 + nanite/recipes/chef.rb | 49 + nfs/attributes/server.rb | 2 + nfs/metadata.json | 46 + nfs/metadata.rb | 4 + nfs/recipes/client.rb | 22 + nfs/recipes/default.rb | 1 + nfs/recipes/server.rb | 9 + nfs/templates/default/exports.erb | 14 + nginx/README.rdoc | 61 + nginx/attributes/nginx.rb | 44 + nginx/definitions/nginx_site.rb | 35 + nginx/files/default/mime.types | 73 + nginx/metadata.json | 203 + nginx/metadata.rb | 83 + nginx/recipes/default.rb | 56 + nginx/recipes/source.rb | 99 + nginx/templates/default/default-site.erb | 11 + nginx/templates/default/nginx.conf.erb | 40 + nginx/templates/default/nxdissite.erb | 29 + nginx/templates/default/nxensite.erb | 38 + nginx/templates/default/sv-nginx-log-run.erb | 2 + nginx/templates/default/sv-nginx-run.erb | 3 + nscd/metadata.json | 51 + nscd/metadata.rb | 9 + nscd/recipes/default.rb | 33 + ntp/attributes/ntp.rb | 9 + ntp/metadata.json | 97 + ntp/metadata.rb | 31 + ntp/recipes/default.rb | 41 + ntp/templates/default/ntp.conf.erb | 21 + openldap/README.rdoc | 101 + openldap/Rakefile | 48 + openldap/attributes/openldap.rb | 61 + openldap/files/default/common-account | 7 + openldap/files/default/common-auth | 7 + openldap/files/default/common-password | 7 + openldap/files/default/common-session | 9 + openldap/files/default/nsswitch.conf | 21 + openldap/files/default/slapd.seed | 21 + openldap/metadata.json | 235 + openldap/metadata.rb | 98 + openldap/recipes/auth.rb | 70 + openldap/recipes/client.rb | 28 + openldap/recipes/default.rb | 18 + openldap/recipes/server.rb | 111 + openldap/templates/default/default_slapd.erb | 47 + openldap/templates/default/ldap-ldap.conf.erb | 16 + openldap/templates/default/ldap.conf.erb | 26 + .../templates/default/libnss-ldap.conf.erb | 23 + .../templates/default/login_access.conf.erb | 16 + openldap/templates/default/slapd.conf.erb | 126 + openssh/metadata.json | 52 + openssh/metadata.rb | 9 + openssh/recipes/default.rb | 41 + openssh/templates/default/port_ssh.erb | 2 + openssl/README.rdoc | 33 + openssl/libraries/secure_password.rb | 37 + openssl/metadata.rb | 6 + openssl/recipes/default.rb | 19 + openvpn/README.rdoc | 69 + openvpn/Rakefile | 93 + openvpn/attributes/openvpn.rb | 24 + openvpn/files/default/easy-rsa/Makefile | 13 + openvpn/files/default/easy-rsa/README.gz | Bin 0 -> 3745 bytes openvpn/files/default/easy-rsa/build-ca | 8 + openvpn/files/default/easy-rsa/build-dh | 11 + openvpn/files/default/easy-rsa/build-inter | 7 + openvpn/files/default/easy-rsa/build-key | 7 + openvpn/files/default/easy-rsa/build-key-pass | 7 + .../files/default/easy-rsa/build-key-pkcs12 | 8 + .../files/default/easy-rsa/build-key-server | 10 + openvpn/files/default/easy-rsa/build-req | 7 + openvpn/files/default/easy-rsa/build-req-pass | 7 + openvpn/files/default/easy-rsa/clean-all | 16 + openvpn/files/default/easy-rsa/inherit-inter | 39 + openvpn/files/default/easy-rsa/list-crl | 13 + .../default/easy-rsa/openssl-0.9.6.cnf.gz | Bin 0 -> 2976 bytes openvpn/files/default/easy-rsa/openssl.cnf | 285 + openvpn/files/default/easy-rsa/pkitool | 358 + openvpn/files/default/easy-rsa/revoke-full | 39 + openvpn/files/default/easy-rsa/sign-req | 7 + openvpn/files/default/easy-rsa/vars | 68 + .../files/default/easy-rsa/whichopensslcnf | 13 + openvpn/files/default/keys/README | 1 + openvpn/metadata.json | 106 + openvpn/metadata.rb | 36 + openvpn/recipes/default.rb | 65 + openvpn/templates/default/server.conf.erb | 42 + openvpn/templates/default/server.up.sh.erb | 9 + packages/attributes/packages.rb | 3 + packages/libraries/packages.rb | 9 + packages/metadata.json | 63 + packages/metadata.rb | 20 + packages/recipes/default.rb | 0 passenger/attributes/passenger.rb | 8 + passenger/definitions/passenger_monitor.rb | 11 + passenger/files/default/passenger_monitor | 50 + passenger/files/default/shadow-1.4.1/HISTORY | 34 + passenger/files/default/shadow-1.4.1/MANIFEST | 7 + passenger/files/default/shadow-1.4.1/README | 79 + .../files/default/shadow-1.4.1/README.euc | 80 + passenger/files/default/shadow-1.4.1/depend | 1 + .../files/default/shadow-1.4.1/extconf.rb | 26 + passenger/files/default/shadow-1.4.1/shadow.c | 281 + passenger/metadata.json | 40 + passenger/metadata.rb | 5 + passenger/recipes/default.rb | 45 + .../templates/default/application.vhost.erb | 70 + .../templates/default/passenger.conf.erb | 6 + .../templates/default/passenger.load.erb | 1 + passenger_apache2/README.rdoc | 65 + passenger_apache2/attributes/passenger.rb | 3 + passenger_apache2/metadata.json | 96 + passenger_apache2/metadata.rb | 29 + passenger_apache2/recipes/default.rb | 52 + passenger_apache2/recipes/mod_rails.rb | 54 + .../templates/default/passenger.conf.erb | 2 + .../templates/default/passenger.load.erb | 1 + .../default/passenger_web_app.conf.erb | 34 + .../templates/default/web_app.conf.erb | 34 + passenger_enterprise/README.rdoc | 8 + .../attributes/passenger_enterprise.rb | 28 + passenger_enterprise/metadata.json | 59 + passenger_enterprise/metadata.rb | 12 + passenger_enterprise/recipes/apache2.rb | 54 + passenger_enterprise/recipes/default.rb | 30 + passenger_enterprise/recipes/nginx.rb | 50 + .../templates/default/passenger.conf.erb | 2 + .../templates/default/passenger.load.erb | 1 + .../default/passenger_nginx.conf.erb | 2 + perl/README.rdoc | 47 + perl/definitions/cpan_module.rb | 31 + perl/files/default/Config-5.10.0.pm | 64 + perl/files/default/Config-5.8.8.pm | 43 + perl/files/default/cpan_install | 69 + perl/metadata.json | 49 + perl/metadata.rb | 10 + perl/recipes/default.rb | 65 + perlbal/attributes/perlbal.rb | 4 + perlbal/metadata.json | 43 + perlbal/metadata.rb | 6 + perlbal/recipes/default.rb | 17 + .../default/bluepill_perlbal.conf.erb | 11 + perlbal/templates/default/god.conf.erb | 32 + perlbal/templates/default/perlbal.conf.erb | 21 + .../templates/default/sv-perlbal-log-run.erb | 2 + perlbal/templates/default/sv-perlbal-run.erb | 3 + php/definitions/pear.rb | 43 + php/definitions/pear_channel.rb | 31 + php/definitions/pear_module.rb | 31 + php/definitions/php_app.rb | 42 + php/files/default/apache2-php5.ini | 1251 ++ php/metadata.json | 101 + php/metadata.rb | 24 + php/recipes/default.rb | 19 + php/recipes/module_apc.rb | 23 + php/recipes/module_curl.rb | 23 + php/recipes/module_fileinfo.rb | 23 + php/recipes/module_fpdf.rb | 23 + php/recipes/module_gd.rb | 23 + php/recipes/module_ldap.rb | 23 + php/recipes/module_memcache.rb | 23 + php/recipes/module_mysql.rb | 23 + php/recipes/module_pgsql.rb | 23 + php/recipes/module_sqlite3.rb | 23 + php/recipes/pear.rb | 23 + php/recipes/php4.rb | 25 + php/recipes/php5-cgi.rb | 30 + php/recipes/php5.rb | 41 + php/templates/default/php.conf.erb | 63 + php5/attributes/php5.rb | 5 + php5/metadata.json | 44 + php5/metadata.rb | 5 + php5/recipes/default.rb | 32 + php5/recipes/sites.rb | 39 + php5/templates/default/php.ini.erb | 3 + php5/templates/default/php5.conf.erb | 4 + php5/templates/default/php5.load.erb | 3 + postfix/attributes/postfix.rb | 14 + postfix/metadata.json | 199 + postfix/metadata.rb | 81 + postfix/recipes/default.rb | 37 + postfix/recipes/sasl_auth.rb | 40 + postfix/templates/default/main.cf.erb | 37 + postfix/templates/default/master.cf.erb | 79 + postfix/templates/default/sasl_passwd.erb | 1 + postgresql/README.rdoc | 45 + postgresql/attributes/postgresql.rb | 27 + postgresql/metadata.json | 67 + postgresql/metadata.rb | 18 + postgresql/recipes/client.rb | 25 + postgresql/recipes/default.rb | 18 + postgresql/recipes/server.rb | 47 + postgresql/templates/default/pg_hba.conf.erb | 83 + .../templates/default/postgresql.conf.erb | 493 + python/metadata.json | 43 + python/metadata.rb | 9 + python/recipes/default.rb | 30 + quick_start/attributes/quick_start.rb | 1 + quick_start/metadata.json | 74 + quick_start/metadata.rb | 19 + quick_start/recipes/default.rb | 24 + .../templates/default/deep_thought.txt.erb | 1 + rabbitmq/metadata.json | 52 + rabbitmq/metadata.rb | 9 + rabbitmq/recipes/default.rb | 13 + radiant/README.rdoc | 74 + radiant/attributes/radiant.rb | 27 + radiant/libraries/radiant.rb | 7 + radiant/metadata.json | 125 + radiant/metadata.rb | 44 + radiant/recipes/default.rb | 95 + radiant/templates/default/database.yml.erb | 4 + radiant/templates/default/radiant.conf.erb | 21 + rails/README.rdoc | 54 + rails/attributes/rails.rb | 3 + rails/metadata.json | 99 + rails/metadata.rb | 35 + rails/recipes/default.rb | 31 + rails/templates/default/rails_app.conf.erb | 30 + rails_enterprise/README.rdoc | 24 + rails_enterprise/metadata.json | 45 + rails_enterprise/metadata.rb | 12 + rails_enterprise/recipes/default.rb | 29 + redmine/README.rdoc | 58 + redmine/attributes/redmine.rb | 36 + redmine/metadata.json | 57 + redmine/metadata.rb | 13 + redmine/recipes/default.rb | 74 + redmine/templates/default/database.yml.erb | 22 + redmine/templates/default/port_redmine.erb | 2 + redmine/templates/default/redmine.conf.erb | 19 + redmine/templates/default/settings.yml.erb | 142 + .../templates/default/sv-redmine-log-run.erb | 2 + redmine/templates/default/sv-redmine-run.erb | 3 + resolver/attributes/resolver.rb | 2 + resolver/metadata.json | 94 + resolver/metadata.rb | 26 + resolver/recipes/default.rb | 27 + resolver/templates/default/resolv.conf.erb | 4 + rsnapshot/attributes/rsnapshot.rb | 1 + rsnapshot/recipes/client.rb | 2 + rsnapshot/recipes/server.rb | 5 + .../templates/default/rsnapshot.conf.erb | 54 + rsync/metadata.json | 52 + rsync/metadata.rb | 9 + rsync/recipes/default.rb | 20 + rsyslog/README.rdoc | 55 + rsyslog/attributes/rsyslog.rb | 22 + rsyslog/files/default/rsyslog.default | 9 + rsyslog/files/ubuntu-9.10/rsyslog.default | 9 + rsyslog/metadata.json | 93 + rsyslog/metadata.rb | 32 + rsyslog/recipes/client.rb | 40 + rsyslog/recipes/default.rb | 61 + rsyslog/recipes/server.rb | 51 + rsyslog/templates/default/remote.conf.erb | 6 + rsyslog/templates/default/rsyslog.conf.erb | 115 + rsyslog/templates/default/rsyslog_gz.erb | 2 + rsyslog/templates/default/server.conf.erb | 58 + .../templates/ubuntu-9.10/50-default.conf.erb | 69 + rsyslog/templates/ubuntu-9.10/remote.conf.erb | 6 + rsyslog/templates/ubuntu-9.10/server.conf.erb | 58 + ruby-shadow/attributes/ruby-shadow.rb | 1 + .../files/default/shadow-1.4.1/HISTORY | 34 + .../files/default/shadow-1.4.1/MANIFEST | 7 + ruby-shadow/files/default/shadow-1.4.1/README | 79 + .../files/default/shadow-1.4.1/README.euc | 80 + ruby-shadow/files/default/shadow-1.4.1/depend | 1 + .../files/default/shadow-1.4.1/extconf.rb | 26 + .../files/default/shadow-1.4.1/shadow.c | 281 + ruby-shadow/recipes/default.rb | 15 + ruby/metadata.json | 52 + ruby/metadata.rb | 9 + ruby/recipes/default.rb | 49 + ruby_enterprise/README.rdoc | 53 + ruby_enterprise/attributes/ruby_enterprise.rb | 30 + ruby_enterprise/definitions/ree_gem.rb | 28 + ruby_enterprise/metadata.json | 45 + ruby_enterprise/metadata.rb | 9 + ruby_enterprise/recipes/default.rb | 49 + ruby_enterprise_edition/attributes/ree.rb | 3 + ruby_enterprise_edition/recipes/default.rb | 9 + rubygems/metadata.rb | 9 + rubygems/recipes/default.rb | 25 + runit/attributes/sv_bin.rb | 25 + runit/definitions/runit_service.rb | 87 + runit/files/default/runsvdir | 0 runit/files/ubuntu-6.10/runsvdir | 6 + runit/files/ubuntu-7.04/runsvdir | 7 + runit/files/ubuntu-7.10/runsvdir | 7 + runit/files/ubuntu-8.04/runsvdir | 7 + runit/metadata.json | 85 + runit/metadata.rb | 30 + runit/recipes/default.rb | 46 + screen/metadata.json | 52 + screen/metadata.rb | 9 + screen/recipes/default.rb | 22 + .../attributes/simple_rails_app.rb | 2 + simple_rails_app/recipes/default.rb | 51 + .../templates/default/post-receive.erb | 23 + .../templates/default/rails_app_conf.erb | 13 + solr/README.rdoc | 57 + solr/attributes/solr.rb | 23 + solr/definitions/solr_instance.rb | 65 + solr/files/default/authorized_keys | 0 solr/files/default/id_rsa | 0 solr/files/default/id_rsa.pub | 0 solr/metadata.json | 96 + solr/metadata.rb | 36 + solr/recipes/default.rb | 36 + solr/templates/default/sv-solr-log-run.erb | 2 + solr/templates/default/sv-solr-run.erb | 8 + sqlite/metadata.json | 43 + sqlite/metadata.rb | 9 + sqlite/recipes/default.rb | 26 + ssh/attributes/ssh.rb | 2 + ssh/files/default/known_hosts | 1 + ssh/metadata.json | 38 + ssh/metadata.rb | 4 + ssh/recipes/server.rb | 29 + ssh/templates/default/ssh_config.erb | 8 + ssh/templates/default/sshd_config.erb | 45 + ssh_keys/definitions/add_keys.rb | 17 + ssh_keys/recipes/default.rb | 0 .../templates/default/authorized_keys.erb | 4 + ssh_known_hosts/README.rdoc | 29 + ssh_known_hosts/metadata.json | 53 + ssh_known_hosts/metadata.rb | 11 + ssh_known_hosts/recipes/default.rb | 31 + .../templates/default/known_hosts.erb | 6 + .../attributes/ssl_certificates.rb | 1 + .../definitions/ssl_certificate.rb | 34 + ssl_certificates/metadata.json | 38 + ssl_certificates/metadata.rb | 4 + ssl_certificates/recipes/default.rb | 5 + stompserver/metadata.json | 48 + stompserver/metadata.rb | 13 + stompserver/recipes/default.rb | 44 + .../templates/default/port_stompserver.erb | 2 + .../default/sv-stompserver-log-run.erb | 3 + .../templates/default/sv-stompserver-run.erb | 4 + subversion/metadata.json | 52 + subversion/metadata.rb | 9 + subversion/recipes/default.rb | 39 + sudo/attributes/sudoers.rb | 21 + sudo/metadata.json | 96 + sudo/metadata.rb | 31 + sudo/recipes/default.rb | 33 + sudo/templates/default/sudoers.erb | 22 + sysadmin/files/default/memory_stats | 279 + sysadmin/recipes/default.rb | 39 + sysctl/attributes/sysctl.rb | 1 + sysctl/recipes/default.rb | 8 + .../default/60-custom-settings.conf.erb | 6 + syslog/attributes/default.rb | 2 + syslog/files/default/logsort | Bin 0 -> 19184 bytes syslog/libraries/default.rb | 3 + syslog/metadata.json | 48 + syslog/metadata.rb | 5 + syslog/recipes/client.rb | 10 + syslog/recipes/default.rb | 8 + syslog/recipes/server.rb | 51 + .../default/syslog-ng-client.conf.erb | 29 + .../default/syslog-ng-server.conf.erb | 152 + teamspeak/metadata.json | 42 + teamspeak/metadata.rb | 7 + teamspeak/recipes/default.rb | 73 + teamspeak/templates/default/demo.php.erb | 94 + .../templates/default/port_teamspeak.erb | 4 + teamspeak/templates/default/server.ini.erb | 38 + .../templates/default/teamspeak.conf.erb | 30 + thin/attributes/thin.rb | 2 + thin/metadata.rb | 7 + thin/recipes/default.rb | 3 + thin/templates/default/bluepill.conf.erb | 36 + thrift/README.rdoc | 42 + thrift/metadata.json | 51 + thrift/metadata.rb | 12 + thrift/recipes/default.rb | 40 + timezone/attributes/timezone.rb | 2 + timezone/metadata.json | 38 + timezone/metadata.rb | 4 + timezone/recipes/default.rb | 5 + tomcat/attributes/default.rb | 6 + tomcat/metadata.json | 40 + tomcat/metadata.rb | 5 + tomcat/recipes/default.rb | 29 + tomcat/templates/default/default.tomcat6.erb | 30 + tomcat/templates/default/server.xml.erb | 24 + tomcat6/README.rdoc | 38 + tomcat6/attributes/tomcat6.rb | 41 + tomcat6/definitions/tomcat_app.rb | 2 + tomcat6/files/centos/rightscale.repo | 15 + tomcat6/files/default/JVM-MANAGEMENT-MIB.mib | 3246 +++++ tomcat6/files/default/dtomcat6 | 81 + tomcat6/files/default/logging.properties | 73 + tomcat6/files/default/tomcat6 | 321 + tomcat6/libraries/tomcat.rb | 58 + tomcat6/libraries/tomcat_manager.rb | 233 + tomcat6/metadata.json | 60 + tomcat6/metadata.rb | 17 + tomcat6/recipes/default.rb | 197 + tomcat6/templates/default/manager.xml.erb | 21 + .../templates/default/tomcat-users.xml.erb | 5 + tomcat6/templates/default/tomcat6.conf.erb | 55 + ubuntu/metadata.json | 42 + ubuntu/metadata.rb | 7 + ubuntu/recipes/default.rb | 27 + ubuntu/templates/default/sources.list.erb | 15 + unicorn/README.rdoc | 17 + unicorn/definitions/unicorn_config.rb | 49 + unicorn/metadata.rb | 8 + unicorn/recipes/default.rb | 24 + unicorn/templates/default/unicorn.rb.erb | 48 + users/attributes/default.rb | 4 + users/definitions/add_keys.rb | 38 + users/libraries/roles.rb | 17 + users/metadata.json | 38 + users/metadata.rb | 4 + users/recipes/default.rb | 17 + users/templates/default/authorized_keys.erb | 4 + varnish/attributes/varnish.rb | 5 + varnish/metadata.json | 43 + varnish/metadata.rb | 9 + varnish/recipes/default.rb | 44 + varnish/templates/default/default.vcl.erb | 11 + varnish/templates/default/ubuntu-default.erb | 92 + xen/create_slice/example | 24 + xen/create_slice/libraries/default.rb | 3 + .../templates/default/slice_config.xen.erb | 10 + xen/metadata.json | 36 + xen/metadata.rb | 4 + xfs/README.rdoc | 24 + xfs/metadata.json | 46 + xfs/metadata.rb | 10 + xfs/recipes/default.rb | 22 + zsh/metadata.json | 43 + zsh/metadata.rb | 9 + zsh/recipes/default.rb | 29 + 929 files changed, 50940 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 NOTICE create mode 100644 Rakefile create mode 100644 activemq/README.rdoc create mode 100644 activemq/attributes/activemq.rb create mode 100644 activemq/metadata.json create mode 100644 activemq/metadata.rb create mode 100644 activemq/recipes/default.rb create mode 100644 activemq/templates/default/sv-activemq-log-run.erb create mode 100644 activemq/templates/default/sv-activemq-run.erb create mode 100644 apache2/README.rdoc create mode 100644 apache2/attributes/apache.rb create mode 100644 apache2/definitions/apache_conf.rb create mode 100644 apache2/definitions/apache_module.rb create mode 100644 apache2/definitions/apache_site.rb create mode 100644 apache2/definitions/web_app.rb create mode 100644 apache2/files/default/apache2_module_conf_generate.pl create mode 100644 apache2/metadata.json create mode 100644 apache2/metadata.rb create mode 100644 apache2/recipes/default.rb create mode 100644 apache2/recipes/god_monitor.rb create mode 100644 apache2/recipes/mod_alias.rb create mode 100644 apache2/recipes/mod_auth_basic.rb create mode 100644 apache2/recipes/mod_auth_digest.rb create mode 100644 apache2/recipes/mod_auth_openid.rb create mode 100644 apache2/recipes/mod_authn_file.rb create mode 100644 apache2/recipes/mod_authnz_ldap.rb create mode 100644 apache2/recipes/mod_authz_default.rb create mode 100644 apache2/recipes/mod_authz_groupfile.rb create mode 100644 apache2/recipes/mod_authz_host.rb create mode 100644 apache2/recipes/mod_authz_user.rb create mode 100644 apache2/recipes/mod_autoindex.rb create mode 100644 apache2/recipes/mod_cgi.rb create mode 100644 apache2/recipes/mod_dav.rb create mode 100644 apache2/recipes/mod_dav_svn.rb create mode 100644 apache2/recipes/mod_deflate.rb create mode 100644 apache2/recipes/mod_dir.rb create mode 100644 apache2/recipes/mod_env.rb create mode 100644 apache2/recipes/mod_expires.rb create mode 100644 apache2/recipes/mod_fcgid.rb create mode 100644 apache2/recipes/mod_headers.rb create mode 100644 apache2/recipes/mod_ldap.rb create mode 100644 apache2/recipes/mod_log_config.rb create mode 100644 apache2/recipes/mod_mime.rb create mode 100644 apache2/recipes/mod_negotiation.rb create mode 100644 apache2/recipes/mod_php5.rb create mode 100644 apache2/recipes/mod_proxy.rb create mode 100644 apache2/recipes/mod_proxy_ajp.rb create mode 100644 apache2/recipes/mod_proxy_balancer.rb create mode 100644 apache2/recipes/mod_proxy_connect.rb create mode 100644 apache2/recipes/mod_proxy_http.rb create mode 100644 apache2/recipes/mod_python.rb create mode 100644 apache2/recipes/mod_rewrite.rb create mode 100644 apache2/recipes/mod_setenvif.rb create mode 100644 apache2/recipes/mod_ssl.rb create mode 100644 apache2/recipes/mod_status.rb create mode 100644 apache2/templates/default/a2dismod.erb create mode 100644 apache2/templates/default/a2dissite.erb create mode 100644 apache2/templates/default/a2enmod.erb create mode 100644 apache2/templates/default/a2ensite.erb create mode 100644 apache2/templates/default/apache2.conf.erb create mode 100644 apache2/templates/default/apache2.god.erb create mode 100644 apache2/templates/default/charset.erb create mode 100644 apache2/templates/default/default-site.erb create mode 100644 apache2/templates/default/mod_auth_openid.rb.erb create mode 100644 apache2/templates/default/mods/README create mode 100644 apache2/templates/default/mods/alias.conf.erb create mode 100644 apache2/templates/default/mods/authopenid.load.erb create mode 100644 apache2/templates/default/mods/autoindex.conf.erb create mode 100644 apache2/templates/default/mods/deflate.conf.erb create mode 100644 apache2/templates/default/mods/dir.conf.erb create mode 100644 apache2/templates/default/mods/fcgid.conf.erb create mode 100644 apache2/templates/default/mods/mime.conf.erb create mode 100644 apache2/templates/default/mods/negotiation.conf.erb create mode 100644 apache2/templates/default/mods/proxy.conf.erb create mode 100644 apache2/templates/default/mods/setenvif.conf.erb create mode 100644 apache2/templates/default/mods/ssl.conf.erb create mode 100644 apache2/templates/default/mods/status.conf.erb create mode 100644 apache2/templates/default/port_apache.erb create mode 100644 apache2/templates/default/ports.conf.erb create mode 100644 apache2/templates/default/security.erb create mode 100644 apache2/templates/default/web_app.conf.erb create mode 100644 apparmor/metadata.json create mode 100644 apparmor/metadata.rb create mode 100644 apparmor/recipes/default.rb create mode 100644 apt/files/default/apt-cacher create mode 100644 apt/files/default/apt-cacher.conf create mode 100644 apt/files/default/apt-proxy-v2.conf create mode 100644 apt/metadata.json create mode 100644 apt/metadata.rb create mode 100644 apt/recipes/cacher.rb create mode 100644 apt/recipes/default.rb create mode 100644 apt/recipes/proxy.rb create mode 100644 aws/libraries/ec2.rb create mode 100644 aws/providers/ebs_volume.rb create mode 100644 aws/providers/elastic_ip.rb create mode 100644 aws/recipes/default.rb create mode 100644 aws/resources/ebs_volume.rb create mode 100644 aws/resources/elastic_ip.rb create mode 100755 bluepill/attributes/bluepill.rb create mode 100755 bluepill/definitions/bluepill_monitor.rb create mode 100755 bluepill/metadata.json create mode 100755 bluepill/metadata.rb create mode 100755 bluepill/recipes/default.rb create mode 100755 bluepill/templates/default/init.sh.erb create mode 100644 boost/README.rdoc create mode 100644 boost/metadata.json create mode 100644 boost/metadata.rb create mode 100644 boost/recipes/default.rb create mode 100644 bootstrap/README.rdoc create mode 100644 bootstrap/attributes/bootstrap.rb create mode 100644 bootstrap/metadata.json create mode 100644 bootstrap/metadata.rb create mode 100644 bootstrap/recipes/client.rb create mode 100644 bootstrap/recipes/default.rb create mode 100644 bootstrap/recipes/server.rb create mode 100644 bootstrap/templates/default/client.rb.erb create mode 100644 bootstrap/templates/default/server.rb.erb create mode 100644 bootstrap/templates/default/sv-chef-client-log-run.erb create mode 100644 bootstrap/templates/default/sv-chef-client-run.erb create mode 100644 bootstrap/templates/default/sv-chef-indexer-log-run.erb create mode 100644 bootstrap/templates/default/sv-chef-indexer-run.erb create mode 100644 bootstrap/templates/default/sv-chef-server-log-run.erb create mode 100644 bootstrap/templates/default/sv-chef-server-run.erb create mode 100644 build-essential/metadata.json create mode 100644 build-essential/metadata.rb create mode 100644 build-essential/recipes/default.rb create mode 100755 build/attributes/build.rb create mode 100755 build/metadata.json create mode 100755 build/metadata.rb create mode 100755 build/recipes/default.rb create mode 100644 capistrano/definitions/cap_setup.rb create mode 100644 capistrano/metadata.json create mode 100644 capistrano/metadata.rb create mode 100644 capistrano/recipes/default.rb create mode 100644 chef/README.rdoc create mode 100644 chef/attributes/chef.rb create mode 100644 chef/attributes/server_proxy.rb create mode 100644 chef/metadata.json create mode 100644 chef/metadata.rb create mode 100644 chef/recipes/client.rb create mode 100644 chef/recipes/default.rb create mode 100644 chef/recipes/server.rb create mode 100644 chef/recipes/server_proxy.rb create mode 100644 chef/templates/default/chef_server.conf.erb create mode 100644 chef/templates/default/client.rb.erb create mode 100644 chef/templates/default/server.rb.erb create mode 100755 collectd/attributes/collectd.rb create mode 100755 collectd/libraries/default.rb create mode 100755 collectd/metadata.json create mode 100755 collectd/metadata.rb create mode 100755 collectd/recipes/default.rb create mode 100755 collectd/templates/default/collectd.conf.erb create mode 100755 collectd/templates/default/collection.conf.erb create mode 100755 collectd/templates/default/thresholds.conf.erb create mode 100644 couchdb/metadata.json create mode 100644 couchdb/metadata.rb create mode 100644 couchdb/recipes/default.rb create mode 100644 couchdb/templates/default/port_couchdb.erb create mode 100755 cron/attributes/cron.rb create mode 100755 cron/metadata.json create mode 100755 cron/metadata.rb create mode 100755 cron/recipes/default.rb create mode 100755 ddclient/metadata.json create mode 100755 ddclient/metadata.rb create mode 100755 ddclient/recipes/default.rb create mode 100755 ddclient/templates/default/ddclient.conf.erb create mode 100755 ddclient/templates/default/ddclient.default.erb create mode 100644 django/README.rdoc create mode 100644 django/metadata.json create mode 100644 django/metadata.rb create mode 100644 django/recipes/default.rb create mode 100644 djbdns/attributes/djbdns.rb create mode 100644 djbdns/metadata.json create mode 100644 djbdns/metadata.rb create mode 100644 djbdns/recipes/axfr.rb create mode 100644 djbdns/recipes/cache.rb create mode 100644 djbdns/recipes/default.rb create mode 100644 djbdns/recipes/internal_server.rb create mode 100644 djbdns/recipes/server.rb create mode 100644 djbdns/templates/default/dnscache-servers.erb create mode 100644 djbdns/templates/default/sv-axfrdns-log-run.erb create mode 100644 djbdns/templates/default/sv-axfrdns-run.erb create mode 100644 djbdns/templates/default/sv-public-dnscache-log-run.erb create mode 100644 djbdns/templates/default/sv-public-dnscache-run.erb create mode 100644 djbdns/templates/default/sv-tinydns-internal-log-run.erb create mode 100644 djbdns/templates/default/sv-tinydns-internal-run.erb create mode 100644 djbdns/templates/default/sv-tinydns-log-run.erb create mode 100644 djbdns/templates/default/sv-tinydns-run.erb create mode 100644 djbdns/templates/default/tinydns-data.erb create mode 100644 djbdns/templates/default/tinydns-internal-data.erb create mode 100755 dns/metadata.json create mode 100755 dns/metadata.rb create mode 100755 dns/recipes/client.rb create mode 100755 dns/recipes/default.rb create mode 100755 dns/templates/default/resolv.conf.erb create mode 100644 drbd/metadata.json create mode 100644 drbd/metadata.rb create mode 100644 drbd/recipes/default.rb create mode 100644 dynomite/README.rdoc create mode 100644 dynomite/attributes/dynomite.rb create mode 100644 dynomite/metadata.json create mode 100644 dynomite/metadata.rb create mode 100644 dynomite/recipes/default.rb create mode 100644 dynomite/templates/default/sv-dynomite-log-run.erb create mode 100644 dynomite/templates/default/sv-dynomite-run.erb create mode 100644 ec2/attributes/ec2_recipe_options.rb create mode 100644 ec2/metadata.rb create mode 100644 ec2/recipes/default.rb create mode 100755 ejabberd/metadata.json create mode 100755 ejabberd/metadata.rb create mode 100755 ejabberd/recipes/default.rb create mode 100755 ejabberd/templates/default/ejabberd.cfg.erb create mode 100644 emacs/metadata.json create mode 100644 emacs/metadata.rb create mode 100644 emacs/recipes/default.rb create mode 100644 erlang/attributes/erlang.rb create mode 100644 erlang/metadata.json create mode 100644 erlang/metadata.rb create mode 100644 erlang/recipes/default.rb create mode 100644 fail2ban/metadata.json create mode 100644 fail2ban/metadata.rb create mode 100644 fail2ban/recipes/default.rb create mode 100644 fail2ban/templates/default/fail2ban.conf.erb create mode 100644 fail2ban/templates/default/jail.conf.erb create mode 100755 ganglia/attributes/ganglia.rb create mode 100755 ganglia/metadata.rb create mode 100755 ganglia/recipes/client.rb create mode 100755 ganglia/recipes/default.rb create mode 100755 ganglia/recipes/server.rb create mode 100755 ganglia/templates/default/ganglia-vhost.conf.erb create mode 100755 ganglia/templates/default/gmetad.conf.erb create mode 100755 ganglia/templates/default/gmond.conf.erb create mode 100644 gems/README.rdoc create mode 100644 gems/attributes/gems.rb create mode 100644 gems/metadata.json create mode 100644 gems/metadata.rb create mode 100644 gems/recipes/default.rb create mode 100644 git/README.rdoc create mode 100644 git/metadata.json create mode 100644 git/metadata.rb create mode 100644 git/recipes/default.rb create mode 100644 git/recipes/server.rb create mode 100644 git/templates/default/sv-git-daemon-log-run.erb create mode 100644 git/templates/default/sv-git-daemon-run.erb create mode 100755 gitosis/files/default/access.py create mode 100755 gitosis/files/default/serve.py create mode 100755 gitosis/metadata.json create mode 100755 gitosis/metadata.rb create mode 100755 gitosis/recipes/default.rb create mode 100755 gitweb/attributes/gitweb.rb create mode 100755 gitweb/metadata.json create mode 100755 gitweb/metadata.rb create mode 100755 gitweb/recipes/default.rb create mode 100755 gitweb/templates/default/apache-vhost.conf.erb create mode 100755 gitweb/templates/default/apache.conf.erb create mode 100755 gitweb/templates/default/gitweb.conf.erb create mode 100755 gitweb/templates/default/projects.conf.erb create mode 100644 glassfish/README.rdoc create mode 100644 glassfish/attributes/glassfish.rb create mode 100644 glassfish/metadata.json create mode 100644 glassfish/metadata.rb create mode 100644 glassfish/recipes/default.rb create mode 100644 glassfish/templates/default/answer_file.erb create mode 100644 glassfish/templates/default/glassfish-init.d-script.erb create mode 100755 gnupg/metadata.json create mode 100755 gnupg/metadata.rb create mode 100755 gnupg/recipes/default.rb create mode 100644 god/README.rdoc create mode 100644 god/definitions/god_monitor.rb create mode 100644 god/metadata.json create mode 100644 god/metadata.rb create mode 100644 god/recipes/default.rb create mode 100644 god/templates/default/master.god.erb create mode 100644 god/templates/default/mongrel.god.erb create mode 100644 god/templates/default/sv-god-log-run.erb create mode 100644 god/templates/default/sv-god-run.erb create mode 100644 hadoop/README.rdoc create mode 100644 hadoop/metadata.json create mode 100644 hadoop/metadata.rb create mode 100644 hadoop/recipes/conf_pseudo.rb create mode 100644 hadoop/recipes/default.rb create mode 100644 hadoop/recipes/doc.rb create mode 100644 hadoop/recipes/hive.rb create mode 100644 hadoop/recipes/pig.rb create mode 100644 hadoop/templates/default/cloudera.list.erb create mode 100644 haproxy/README.rdoc create mode 100644 haproxy/metadata.json create mode 100644 haproxy/metadata.rb create mode 100644 haproxy/recipes/default.rb create mode 100644 haproxy/templates/default/haproxy-default.erb create mode 100644 haproxy/templates/default/haproxy.cfg.erb create mode 100644 heartbeat/metadata.json create mode 100644 heartbeat/metadata.rb create mode 100644 heartbeat/recipes/default.rb create mode 100755 hosts/attributes/hosts.rb create mode 100755 hosts/metadata.json create mode 100755 hosts/metadata.rb create mode 100755 hosts/recipes/default.rb create mode 100755 hosts/templates/default/hosts.erb create mode 100644 imagemagick/README.rdoc create mode 100644 imagemagick/metadata.json create mode 100644 imagemagick/metadata.rb create mode 100644 imagemagick/recipes/default.rb create mode 100644 imagemagick/recipes/rmagick.rb create mode 100644 instiki/README.rdoc create mode 100644 instiki/metadata.rb create mode 100644 instiki/recipes/default.rb create mode 100644 instiki/templates/default/instiki.conf.erb create mode 100755 integrity/attributes/integrity.rb create mode 100755 integrity/files/default/integrity_build.rb create mode 100755 integrity/metadata.json create mode 100755 integrity/metadata.rb create mode 100755 integrity/recipes/default.rb create mode 100755 integrity/templates/default/config.ru.erb create mode 100755 integrity/templates/default/config.yml.erb create mode 100755 integrity/templates/default/vhost.conf.erb create mode 100644 iptables/definitions/iptables_rule.rb create mode 100644 iptables/files/default/rebuild-iptables create mode 100644 iptables/metadata.json create mode 100644 iptables/metadata.rb create mode 100644 iptables/recipes/default.rb create mode 100644 iptables/templates/default/restrictive.erb create mode 100644 java/README.rdoc create mode 100644 java/files/default/java.seed create mode 100644 java/metadata.json create mode 100644 java/metadata.rb create mode 100644 java/recipes/default.rb create mode 100644 jira/attributes/jira.rb create mode 100644 jira/files/default/catalina.sh create mode 100644 jira/files/default/startup.sh create mode 100644 jira/metadata.json create mode 100644 jira/metadata.rb create mode 100644 jira/recipes/default.rb create mode 100644 jira/templates/default/apache.conf.erb create mode 100644 jira/templates/default/entityengine.xml.erb create mode 100644 jira/templates/default/server.xml.erb create mode 100644 jira/templates/default/sv-jira-log-run.erb create mode 100644 jira/templates/default/sv-jira-run.erb create mode 100644 keepalived/README.rdoc create mode 100644 keepalived/metadata.json create mode 100644 keepalived/metadata.rb create mode 100644 keepalived/recipes/default.rb create mode 100644 keepalived/templates/default/keepalived.conf.erb create mode 100644 kickstart/README.rdoc create mode 100644 kickstart/attributes/kickstart.rb create mode 100644 kickstart/metadata.json create mode 100644 kickstart/metadata.rb create mode 100644 kickstart/recipes/default.rb create mode 100644 kickstart/recipes/server.rb create mode 100644 kickstart/templates/default/kickstart.conf.erb create mode 100644 kickstart/templates/default/ks.cfg.erb create mode 100755 kvm/files/default/kvmtool create mode 100755 kvm/files/default/modules/2.6.27-11-server/kvm-amd.ko create mode 100755 kvm/files/default/modules/2.6.27-11-server/kvm-intel.ko create mode 100755 kvm/files/default/modules/2.6.27-11-server/kvm.ko create mode 100755 kvm/files/default/templates/files/event.d/ttyS0 create mode 100755 kvm/files/default/templates/files/manifest.txt create mode 100755 kvm/files/default/templates/scripts/add-172-net.sh create mode 100755 kvm/files/default/templates/scripts/grub-console.sh create mode 100755 kvm/files/default/templates/scripts/postinstall.sh create mode 100755 kvm/files/default/templates/scripts/rename-fstab-devices.sh create mode 100755 kvm/files/default/templates/scripts/vmbuild.sh create mode 100755 kvm/files/default/templates/templates/libvirt/libvirtxml.tmpl create mode 100755 kvm/metadata.json create mode 100755 kvm/metadata.rb create mode 100755 kvm/recipes/default.rb create mode 100755 kvm/templates/default/libvirtd.conf.erb create mode 100644 logrotate/metadata.json create mode 100644 logrotate/metadata.rb create mode 100644 logrotate/recipes/default.rb create mode 100644 logwatch/README.rdoc create mode 100644 logwatch/metadata.json create mode 100644 logwatch/metadata.rb create mode 100644 logwatch/recipes/default.rb create mode 100644 lvm/metadata.json create mode 100644 lvm/metadata.rb create mode 100644 lvm/recipes/default.rb create mode 100755 maatkit/files/default/maatkit-5014.tar.gz create mode 100755 maatkit/files/default/mk-query-digest create mode 100755 maatkit/recipes/default.rb create mode 100644 man/metadata.json create mode 100644 man/metadata.rb create mode 100644 man/recipes/default.rb create mode 100644 maradns/attributes/maradns.rb create mode 100644 maradns/metadata.json create mode 100644 maradns/metadata.rb create mode 100644 maradns/recipes/default.rb create mode 100644 maradns/templates/default/mararc.erb create mode 100644 memcached/README.rdoc create mode 100644 memcached/attributes/memcached.rb create mode 100644 memcached/definitions/memcached_instance.rb create mode 100644 memcached/metadata.json create mode 100644 memcached/metadata.rb create mode 100644 memcached/recipes/default.rb create mode 100644 memcached/templates/default/memcached.conf.erb create mode 100644 memcached/templates/default/memcached.default.erb create mode 100644 memcached/templates/default/sv-memcached-log-run.erb create mode 100644 memcached/templates/default/sv-memcached-run.erb create mode 100644 mercurial/metadata.json create mode 100644 mercurial/metadata.rb create mode 100644 mercurial/recipes/default.rb create mode 100755 mogilefs/attributes/mogilefs.rb create mode 100755 mogilefs/metadata.json create mode 100755 mogilefs/metadata.rb create mode 100755 mogilefs/recipes/default.rb create mode 100755 mogilefs/recipes/storenode.rb create mode 100755 mogilefs/recipes/tracker.rb create mode 100755 mogilefs/templates/default/bluepill_mogilefsd.conf.erb create mode 100755 mogilefs/templates/default/bluepill_mogstored.conf.erb create mode 100755 mogilefs/templates/default/mogilefs.conf.erb create mode 100755 mogilefs/templates/default/mogilefs.sh.erb create mode 100755 mogilefs/templates/default/mogilefsd.conf.erb create mode 100755 mogilefs/templates/default/mogstored.conf.erb create mode 100755 mogilefs/templates/default/sv-mogilefsd-log-run.erb create mode 100755 mogilefs/templates/default/sv-mogilefsd-run.erb create mode 100755 mogilefs/templates/default/sv-mogstored-log-run.erb create mode 100755 mogilefs/templates/default/sv-mogstored-run.erb create mode 100755 monit/libraries/monitrc.rb create mode 100755 monit/metadata.json create mode 100755 monit/metadata.rb create mode 100755 monit/recipes/default.rb create mode 100755 monit/templates/default/monitrc.erb create mode 100644 munin/metadata.json create mode 100644 munin/metadata.rb create mode 100644 munin/recipes/default.rb create mode 100644 munin/templates/default/munin-node.conf.erb create mode 100644 munin/templates/default/port_munin.erb create mode 100644 mysql/README.rdoc create mode 100644 mysql/attributes/server.rb create mode 100644 mysql/libraries/database.rb create mode 100644 mysql/metadata.json create mode 100644 mysql/metadata.rb create mode 100644 mysql/providers/database.rb create mode 100644 mysql/recipes/client.rb create mode 100644 mysql/recipes/default.rb create mode 100644 mysql/recipes/server.rb create mode 100644 mysql/recipes/server_ec2.rb create mode 100644 mysql/resources/database.rb create mode 100644 mysql/templates/centos/my.cnf.erb create mode 100644 mysql/templates/default/debian.cnf.erb create mode 100644 mysql/templates/default/grants.sql.erb create mode 100644 mysql/templates/default/my.cnf.erb create mode 100644 mysql/templates/default/mysql-server.seed.erb create mode 100644 mysql/templates/default/port_mysql.erb create mode 100644 mysql/templates/redhat/my.cnf.erb create mode 100644 mysql/templates/ubuntu-8.04/my.cnf.erb create mode 100644 mysql/templates/ubuntu-9.10/my.cnf.erb create mode 100755 nagios/attributes/client.rb create mode 100755 nagios/attributes/server.rb create mode 100755 nagios/definitions/nagios_conf.rb create mode 100755 nagios/definitions/nagios_service_template.rb create mode 100755 nagios/files/default/notifiers/campfire create mode 100755 nagios/files/default/notifiers/clickatell create mode 100755 nagios/files/default/notifiers/jabber create mode 100755 nagios/files/default/notifiers/jabber_bot create mode 100755 nagios/files/default/notifiers/send_passive_check create mode 100755 nagios/files/default/plugins/check-mysql-slave.pl create mode 100755 nagios/files/default/plugins/check_mem.sh create mode 100755 nagios/files/default/plugins/check_mysqlhealth.pl create mode 100755 nagios/files/default/plugins/check_netapp.pl create mode 100755 nagios/files/default/plugins/check_solr.rb create mode 100755 nagios/files/default/plugins/db_queues.rb create mode 100755 nagios/files/default/plugins/file_queues.rb create mode 100755 nagios/files/default/plugins/haproxy_stats.rb create mode 100755 nagios/files/default/plugins/nagios_helper.rb create mode 100755 nagios/libraries/default.rb create mode 100755 nagios/metadata.json create mode 100755 nagios/metadata.rb create mode 100755 nagios/recipes/checks.rb create mode 100755 nagios/recipes/client.rb create mode 100755 nagios/recipes/default.rb create mode 100755 nagios/recipes/server.rb create mode 100755 nagios/templates/default/apache2.conf.erb create mode 100755 nagios/templates/default/commands.cfg.erb create mode 100755 nagios/templates/default/contacts.cfg.erb create mode 100755 nagios/templates/default/hostgroups.cfg.erb create mode 100755 nagios/templates/default/hosts.cfg.erb create mode 100755 nagios/templates/default/nagios.cfg.erb create mode 100755 nagios/templates/default/nrpe.cfg.erb create mode 100755 nagios/templates/default/services.cfg.erb create mode 100755 nagios/templates/default/sv-nagios-bot-log-run.erb create mode 100755 nagios/templates/default/sv-nagios-bot-run.erb create mode 100755 nagios/templates/default/sv-nagios3-log-run.erb create mode 100755 nagios/templates/default/sv-nagios3-run.erb create mode 100755 nagios/templates/default/templates.cfg.erb create mode 100755 nagios/templates/default/timeperiods.cfg.erb create mode 100755 nanite/metadata.json create mode 100755 nanite/recipes/chef.rb create mode 100755 nfs/attributes/server.rb create mode 100755 nfs/metadata.json create mode 100755 nfs/metadata.rb create mode 100755 nfs/recipes/client.rb create mode 100755 nfs/recipes/default.rb create mode 100755 nfs/recipes/server.rb create mode 100755 nfs/templates/default/exports.erb create mode 100644 nginx/README.rdoc create mode 100644 nginx/attributes/nginx.rb create mode 100644 nginx/definitions/nginx_site.rb create mode 100644 nginx/files/default/mime.types create mode 100644 nginx/metadata.json create mode 100644 nginx/metadata.rb create mode 100644 nginx/recipes/default.rb create mode 100644 nginx/recipes/source.rb create mode 100644 nginx/templates/default/default-site.erb create mode 100644 nginx/templates/default/nginx.conf.erb create mode 100644 nginx/templates/default/nxdissite.erb create mode 100644 nginx/templates/default/nxensite.erb create mode 100644 nginx/templates/default/sv-nginx-log-run.erb create mode 100644 nginx/templates/default/sv-nginx-run.erb create mode 100644 nscd/metadata.json create mode 100644 nscd/metadata.rb create mode 100644 nscd/recipes/default.rb create mode 100644 ntp/attributes/ntp.rb create mode 100644 ntp/metadata.json create mode 100644 ntp/metadata.rb create mode 100644 ntp/recipes/default.rb create mode 100644 ntp/templates/default/ntp.conf.erb create mode 100644 openldap/README.rdoc create mode 100644 openldap/Rakefile create mode 100644 openldap/attributes/openldap.rb create mode 100644 openldap/files/default/common-account create mode 100644 openldap/files/default/common-auth create mode 100644 openldap/files/default/common-password create mode 100644 openldap/files/default/common-session create mode 100644 openldap/files/default/nsswitch.conf create mode 100644 openldap/files/default/slapd.seed create mode 100644 openldap/metadata.json create mode 100644 openldap/metadata.rb create mode 100644 openldap/recipes/auth.rb create mode 100644 openldap/recipes/client.rb create mode 100644 openldap/recipes/default.rb create mode 100644 openldap/recipes/server.rb create mode 100644 openldap/templates/default/default_slapd.erb create mode 100644 openldap/templates/default/ldap-ldap.conf.erb create mode 100644 openldap/templates/default/ldap.conf.erb create mode 100644 openldap/templates/default/libnss-ldap.conf.erb create mode 100644 openldap/templates/default/login_access.conf.erb create mode 100644 openldap/templates/default/slapd.conf.erb create mode 100644 openssh/metadata.json create mode 100644 openssh/metadata.rb create mode 100644 openssh/recipes/default.rb create mode 100644 openssh/templates/default/port_ssh.erb create mode 100644 openssl/README.rdoc create mode 100644 openssl/libraries/secure_password.rb create mode 100644 openssl/metadata.rb create mode 100644 openssl/recipes/default.rb create mode 100644 openvpn/README.rdoc create mode 100644 openvpn/Rakefile create mode 100644 openvpn/attributes/openvpn.rb create mode 100644 openvpn/files/default/easy-rsa/Makefile create mode 100644 openvpn/files/default/easy-rsa/README.gz create mode 100755 openvpn/files/default/easy-rsa/build-ca create mode 100755 openvpn/files/default/easy-rsa/build-dh create mode 100755 openvpn/files/default/easy-rsa/build-inter create mode 100755 openvpn/files/default/easy-rsa/build-key create mode 100755 openvpn/files/default/easy-rsa/build-key-pass create mode 100755 openvpn/files/default/easy-rsa/build-key-pkcs12 create mode 100755 openvpn/files/default/easy-rsa/build-key-server create mode 100755 openvpn/files/default/easy-rsa/build-req create mode 100755 openvpn/files/default/easy-rsa/build-req-pass create mode 100755 openvpn/files/default/easy-rsa/clean-all create mode 100755 openvpn/files/default/easy-rsa/inherit-inter create mode 100755 openvpn/files/default/easy-rsa/list-crl create mode 100644 openvpn/files/default/easy-rsa/openssl-0.9.6.cnf.gz create mode 100755 openvpn/files/default/easy-rsa/openssl.cnf create mode 100755 openvpn/files/default/easy-rsa/pkitool create mode 100755 openvpn/files/default/easy-rsa/revoke-full create mode 100755 openvpn/files/default/easy-rsa/sign-req create mode 100755 openvpn/files/default/easy-rsa/vars create mode 100755 openvpn/files/default/easy-rsa/whichopensslcnf create mode 100644 openvpn/files/default/keys/README create mode 100644 openvpn/metadata.json create mode 100644 openvpn/metadata.rb create mode 100644 openvpn/recipes/default.rb create mode 100644 openvpn/templates/default/server.conf.erb create mode 100644 openvpn/templates/default/server.up.sh.erb create mode 100644 packages/attributes/packages.rb create mode 100644 packages/libraries/packages.rb create mode 100644 packages/metadata.json create mode 100644 packages/metadata.rb create mode 100644 packages/recipes/default.rb create mode 100755 passenger/attributes/passenger.rb create mode 100755 passenger/definitions/passenger_monitor.rb create mode 100755 passenger/files/default/passenger_monitor create mode 100755 passenger/files/default/shadow-1.4.1/HISTORY create mode 100755 passenger/files/default/shadow-1.4.1/MANIFEST create mode 100755 passenger/files/default/shadow-1.4.1/README create mode 100755 passenger/files/default/shadow-1.4.1/README.euc create mode 100755 passenger/files/default/shadow-1.4.1/depend create mode 100755 passenger/files/default/shadow-1.4.1/extconf.rb create mode 100755 passenger/files/default/shadow-1.4.1/shadow.c create mode 100755 passenger/metadata.json create mode 100755 passenger/metadata.rb create mode 100755 passenger/recipes/default.rb create mode 100755 passenger/templates/default/application.vhost.erb create mode 100755 passenger/templates/default/passenger.conf.erb create mode 100755 passenger/templates/default/passenger.load.erb create mode 100644 passenger_apache2/README.rdoc create mode 100644 passenger_apache2/attributes/passenger.rb create mode 100644 passenger_apache2/metadata.json create mode 100644 passenger_apache2/metadata.rb create mode 100644 passenger_apache2/recipes/default.rb create mode 100644 passenger_apache2/recipes/mod_rails.rb create mode 100644 passenger_apache2/templates/default/passenger.conf.erb create mode 100644 passenger_apache2/templates/default/passenger.load.erb create mode 100644 passenger_apache2/templates/default/passenger_web_app.conf.erb create mode 100644 passenger_apache2/templates/default/web_app.conf.erb create mode 100644 passenger_enterprise/README.rdoc create mode 100644 passenger_enterprise/attributes/passenger_enterprise.rb create mode 100644 passenger_enterprise/metadata.json create mode 100644 passenger_enterprise/metadata.rb create mode 100644 passenger_enterprise/recipes/apache2.rb create mode 100644 passenger_enterprise/recipes/default.rb create mode 100644 passenger_enterprise/recipes/nginx.rb create mode 100644 passenger_enterprise/templates/default/passenger.conf.erb create mode 100644 passenger_enterprise/templates/default/passenger.load.erb create mode 100644 passenger_enterprise/templates/default/passenger_nginx.conf.erb create mode 100644 perl/README.rdoc create mode 100644 perl/definitions/cpan_module.rb create mode 100644 perl/files/default/Config-5.10.0.pm create mode 100644 perl/files/default/Config-5.8.8.pm create mode 100644 perl/files/default/cpan_install create mode 100644 perl/metadata.json create mode 100644 perl/metadata.rb create mode 100644 perl/recipes/default.rb create mode 100755 perlbal/attributes/perlbal.rb create mode 100755 perlbal/metadata.json create mode 100755 perlbal/metadata.rb create mode 100755 perlbal/recipes/default.rb create mode 100755 perlbal/templates/default/bluepill_perlbal.conf.erb create mode 100755 perlbal/templates/default/god.conf.erb create mode 100755 perlbal/templates/default/perlbal.conf.erb create mode 100755 perlbal/templates/default/sv-perlbal-log-run.erb create mode 100755 perlbal/templates/default/sv-perlbal-run.erb create mode 100644 php/definitions/pear.rb create mode 100644 php/definitions/pear_channel.rb create mode 100644 php/definitions/pear_module.rb create mode 100644 php/definitions/php_app.rb create mode 100644 php/files/default/apache2-php5.ini create mode 100644 php/metadata.json create mode 100644 php/metadata.rb create mode 100644 php/recipes/default.rb create mode 100644 php/recipes/module_apc.rb create mode 100644 php/recipes/module_curl.rb create mode 100644 php/recipes/module_fileinfo.rb create mode 100644 php/recipes/module_fpdf.rb create mode 100644 php/recipes/module_gd.rb create mode 100644 php/recipes/module_ldap.rb create mode 100644 php/recipes/module_memcache.rb create mode 100644 php/recipes/module_mysql.rb create mode 100644 php/recipes/module_pgsql.rb create mode 100644 php/recipes/module_sqlite3.rb create mode 100644 php/recipes/pear.rb create mode 100644 php/recipes/php4.rb create mode 100644 php/recipes/php5-cgi.rb create mode 100644 php/recipes/php5.rb create mode 100644 php/templates/default/php.conf.erb create mode 100755 php5/attributes/php5.rb create mode 100755 php5/metadata.json create mode 100755 php5/metadata.rb create mode 100755 php5/recipes/default.rb create mode 100755 php5/recipes/sites.rb create mode 100755 php5/templates/default/php.ini.erb create mode 100755 php5/templates/default/php5.conf.erb create mode 100755 php5/templates/default/php5.load.erb create mode 100644 postfix/attributes/postfix.rb create mode 100644 postfix/metadata.json create mode 100644 postfix/metadata.rb create mode 100644 postfix/recipes/default.rb create mode 100644 postfix/recipes/sasl_auth.rb create mode 100644 postfix/templates/default/main.cf.erb create mode 100644 postfix/templates/default/master.cf.erb create mode 100644 postfix/templates/default/sasl_passwd.erb create mode 100644 postgresql/README.rdoc create mode 100644 postgresql/attributes/postgresql.rb create mode 100644 postgresql/metadata.json create mode 100644 postgresql/metadata.rb create mode 100644 postgresql/recipes/client.rb create mode 100644 postgresql/recipes/default.rb create mode 100644 postgresql/recipes/server.rb create mode 100644 postgresql/templates/default/pg_hba.conf.erb create mode 100644 postgresql/templates/default/postgresql.conf.erb create mode 100644 python/metadata.json create mode 100644 python/metadata.rb create mode 100644 python/recipes/default.rb create mode 100644 quick_start/attributes/quick_start.rb create mode 100644 quick_start/metadata.json create mode 100644 quick_start/metadata.rb create mode 100644 quick_start/recipes/default.rb create mode 100644 quick_start/templates/default/deep_thought.txt.erb create mode 100755 rabbitmq/metadata.json create mode 100755 rabbitmq/metadata.rb create mode 100755 rabbitmq/recipes/default.rb create mode 100644 radiant/README.rdoc create mode 100644 radiant/attributes/radiant.rb create mode 100644 radiant/libraries/radiant.rb create mode 100644 radiant/metadata.json create mode 100644 radiant/metadata.rb create mode 100644 radiant/recipes/default.rb create mode 100644 radiant/templates/default/database.yml.erb create mode 100644 radiant/templates/default/radiant.conf.erb create mode 100644 rails/README.rdoc create mode 100644 rails/attributes/rails.rb create mode 100644 rails/metadata.json create mode 100644 rails/metadata.rb create mode 100644 rails/recipes/default.rb create mode 100644 rails/templates/default/rails_app.conf.erb create mode 100644 rails_enterprise/README.rdoc create mode 100644 rails_enterprise/metadata.json create mode 100644 rails_enterprise/metadata.rb create mode 100644 rails_enterprise/recipes/default.rb create mode 100644 redmine/README.rdoc create mode 100644 redmine/attributes/redmine.rb create mode 100644 redmine/metadata.json create mode 100644 redmine/metadata.rb create mode 100644 redmine/recipes/default.rb create mode 100644 redmine/templates/default/database.yml.erb create mode 100644 redmine/templates/default/port_redmine.erb create mode 100644 redmine/templates/default/redmine.conf.erb create mode 100644 redmine/templates/default/settings.yml.erb create mode 100644 redmine/templates/default/sv-redmine-log-run.erb create mode 100644 redmine/templates/default/sv-redmine-run.erb create mode 100644 resolver/attributes/resolver.rb create mode 100644 resolver/metadata.json create mode 100644 resolver/metadata.rb create mode 100644 resolver/recipes/default.rb create mode 100644 resolver/templates/default/resolv.conf.erb create mode 100755 rsnapshot/attributes/rsnapshot.rb create mode 100755 rsnapshot/recipes/client.rb create mode 100755 rsnapshot/recipes/server.rb create mode 100755 rsnapshot/templates/default/rsnapshot.conf.erb create mode 100644 rsync/metadata.json create mode 100644 rsync/metadata.rb create mode 100644 rsync/recipes/default.rb create mode 100644 rsyslog/README.rdoc create mode 100644 rsyslog/attributes/rsyslog.rb create mode 100644 rsyslog/files/default/rsyslog.default create mode 100644 rsyslog/files/ubuntu-9.10/rsyslog.default create mode 100644 rsyslog/metadata.json create mode 100644 rsyslog/metadata.rb create mode 100644 rsyslog/recipes/client.rb create mode 100644 rsyslog/recipes/default.rb create mode 100644 rsyslog/recipes/server.rb create mode 100644 rsyslog/templates/default/remote.conf.erb create mode 100644 rsyslog/templates/default/rsyslog.conf.erb create mode 100644 rsyslog/templates/default/rsyslog_gz.erb create mode 100644 rsyslog/templates/default/server.conf.erb create mode 100644 rsyslog/templates/ubuntu-9.10/50-default.conf.erb create mode 100644 rsyslog/templates/ubuntu-9.10/remote.conf.erb create mode 100644 rsyslog/templates/ubuntu-9.10/server.conf.erb create mode 100644 ruby-shadow/attributes/ruby-shadow.rb create mode 100755 ruby-shadow/files/default/shadow-1.4.1/HISTORY create mode 100755 ruby-shadow/files/default/shadow-1.4.1/MANIFEST create mode 100755 ruby-shadow/files/default/shadow-1.4.1/README create mode 100755 ruby-shadow/files/default/shadow-1.4.1/README.euc create mode 100755 ruby-shadow/files/default/shadow-1.4.1/depend create mode 100755 ruby-shadow/files/default/shadow-1.4.1/extconf.rb create mode 100755 ruby-shadow/files/default/shadow-1.4.1/shadow.c create mode 100755 ruby-shadow/recipes/default.rb create mode 100644 ruby/metadata.json create mode 100644 ruby/metadata.rb create mode 100644 ruby/recipes/default.rb create mode 100644 ruby_enterprise/README.rdoc create mode 100644 ruby_enterprise/attributes/ruby_enterprise.rb create mode 100644 ruby_enterprise/definitions/ree_gem.rb create mode 100644 ruby_enterprise/metadata.json create mode 100644 ruby_enterprise/metadata.rb create mode 100644 ruby_enterprise/recipes/default.rb create mode 100755 ruby_enterprise_edition/attributes/ree.rb create mode 100755 ruby_enterprise_edition/recipes/default.rb create mode 100644 rubygems/metadata.rb create mode 100644 rubygems/recipes/default.rb create mode 100644 runit/attributes/sv_bin.rb create mode 100644 runit/definitions/runit_service.rb create mode 100644 runit/files/default/runsvdir create mode 100644 runit/files/ubuntu-6.10/runsvdir create mode 100644 runit/files/ubuntu-7.04/runsvdir create mode 100644 runit/files/ubuntu-7.10/runsvdir create mode 100644 runit/files/ubuntu-8.04/runsvdir create mode 100644 runit/metadata.json create mode 100644 runit/metadata.rb create mode 100644 runit/recipes/default.rb create mode 100644 screen/metadata.json create mode 100644 screen/metadata.rb create mode 100644 screen/recipes/default.rb create mode 100644 simple_rails_app/attributes/simple_rails_app.rb create mode 100644 simple_rails_app/recipes/default.rb create mode 100644 simple_rails_app/templates/default/post-receive.erb create mode 100644 simple_rails_app/templates/default/rails_app_conf.erb create mode 100644 solr/README.rdoc create mode 100644 solr/attributes/solr.rb create mode 100644 solr/definitions/solr_instance.rb create mode 100644 solr/files/default/authorized_keys create mode 100644 solr/files/default/id_rsa create mode 100644 solr/files/default/id_rsa.pub create mode 100644 solr/metadata.json create mode 100644 solr/metadata.rb create mode 100644 solr/recipes/default.rb create mode 100644 solr/templates/default/sv-solr-log-run.erb create mode 100644 solr/templates/default/sv-solr-run.erb create mode 100644 sqlite/metadata.json create mode 100644 sqlite/metadata.rb create mode 100644 sqlite/recipes/default.rb create mode 100755 ssh/attributes/ssh.rb create mode 100755 ssh/files/default/known_hosts create mode 100755 ssh/metadata.json create mode 100755 ssh/metadata.rb create mode 100755 ssh/recipes/server.rb create mode 100755 ssh/templates/default/ssh_config.erb create mode 100755 ssh/templates/default/sshd_config.erb create mode 100755 ssh_keys/definitions/add_keys.rb create mode 100755 ssh_keys/recipes/default.rb create mode 100755 ssh_keys/templates/default/authorized_keys.erb create mode 100644 ssh_known_hosts/README.rdoc create mode 100644 ssh_known_hosts/metadata.json create mode 100644 ssh_known_hosts/metadata.rb create mode 100644 ssh_known_hosts/recipes/default.rb create mode 100644 ssh_known_hosts/templates/default/known_hosts.erb create mode 100755 ssl_certificates/attributes/ssl_certificates.rb create mode 100755 ssl_certificates/definitions/ssl_certificate.rb create mode 100755 ssl_certificates/metadata.json create mode 100755 ssl_certificates/metadata.rb create mode 100755 ssl_certificates/recipes/default.rb create mode 100644 stompserver/metadata.json create mode 100644 stompserver/metadata.rb create mode 100644 stompserver/recipes/default.rb create mode 100644 stompserver/templates/default/port_stompserver.erb create mode 100644 stompserver/templates/default/sv-stompserver-log-run.erb create mode 100644 stompserver/templates/default/sv-stompserver-run.erb create mode 100644 subversion/metadata.json create mode 100644 subversion/metadata.rb create mode 100644 subversion/recipes/default.rb create mode 100644 sudo/attributes/sudoers.rb create mode 100644 sudo/metadata.json create mode 100644 sudo/metadata.rb create mode 100644 sudo/recipes/default.rb create mode 100644 sudo/templates/default/sudoers.erb create mode 100755 sysadmin/files/default/memory_stats create mode 100755 sysadmin/recipes/default.rb create mode 100755 sysctl/attributes/sysctl.rb create mode 100755 sysctl/recipes/default.rb create mode 100755 sysctl/templates/default/60-custom-settings.conf.erb create mode 100755 syslog/attributes/default.rb create mode 100755 syslog/files/default/logsort create mode 100755 syslog/libraries/default.rb create mode 100755 syslog/metadata.json create mode 100755 syslog/metadata.rb create mode 100755 syslog/recipes/client.rb create mode 100755 syslog/recipes/default.rb create mode 100755 syslog/recipes/server.rb create mode 100755 syslog/templates/default/syslog-ng-client.conf.erb create mode 100755 syslog/templates/default/syslog-ng-server.conf.erb create mode 100644 teamspeak/metadata.json create mode 100644 teamspeak/metadata.rb create mode 100644 teamspeak/recipes/default.rb create mode 100644 teamspeak/templates/default/demo.php.erb create mode 100644 teamspeak/templates/default/port_teamspeak.erb create mode 100644 teamspeak/templates/default/server.ini.erb create mode 100644 teamspeak/templates/default/teamspeak.conf.erb create mode 100755 thin/attributes/thin.rb create mode 100755 thin/metadata.rb create mode 100755 thin/recipes/default.rb create mode 100755 thin/templates/default/bluepill.conf.erb create mode 100644 thrift/README.rdoc create mode 100644 thrift/metadata.json create mode 100644 thrift/metadata.rb create mode 100644 thrift/recipes/default.rb create mode 100755 timezone/attributes/timezone.rb create mode 100755 timezone/metadata.json create mode 100755 timezone/metadata.rb create mode 100755 timezone/recipes/default.rb create mode 100755 tomcat/attributes/default.rb create mode 100755 tomcat/metadata.json create mode 100755 tomcat/metadata.rb create mode 100755 tomcat/recipes/default.rb create mode 100755 tomcat/templates/default/default.tomcat6.erb create mode 100755 tomcat/templates/default/server.xml.erb create mode 100644 tomcat6/README.rdoc create mode 100644 tomcat6/attributes/tomcat6.rb create mode 100644 tomcat6/definitions/tomcat_app.rb create mode 100644 tomcat6/files/centos/rightscale.repo create mode 100644 tomcat6/files/default/JVM-MANAGEMENT-MIB.mib create mode 100755 tomcat6/files/default/dtomcat6 create mode 100644 tomcat6/files/default/logging.properties create mode 100644 tomcat6/files/default/tomcat6 create mode 100644 tomcat6/libraries/tomcat.rb create mode 100644 tomcat6/libraries/tomcat_manager.rb create mode 100644 tomcat6/metadata.json create mode 100644 tomcat6/metadata.rb create mode 100644 tomcat6/recipes/default.rb create mode 100644 tomcat6/templates/default/manager.xml.erb create mode 100644 tomcat6/templates/default/tomcat-users.xml.erb create mode 100644 tomcat6/templates/default/tomcat6.conf.erb create mode 100644 ubuntu/metadata.json create mode 100644 ubuntu/metadata.rb create mode 100644 ubuntu/recipes/default.rb create mode 100644 ubuntu/templates/default/sources.list.erb create mode 100644 unicorn/README.rdoc create mode 100644 unicorn/definitions/unicorn_config.rb create mode 100644 unicorn/metadata.rb create mode 100644 unicorn/recipes/default.rb create mode 100644 unicorn/templates/default/unicorn.rb.erb create mode 100755 users/attributes/default.rb create mode 100755 users/definitions/add_keys.rb create mode 100755 users/libraries/roles.rb create mode 100755 users/metadata.json create mode 100755 users/metadata.rb create mode 100755 users/recipes/default.rb create mode 100755 users/templates/default/authorized_keys.erb create mode 100644 varnish/attributes/varnish.rb create mode 100644 varnish/metadata.json create mode 100644 varnish/metadata.rb create mode 100644 varnish/recipes/default.rb create mode 100644 varnish/templates/default/default.vcl.erb create mode 100644 varnish/templates/default/ubuntu-default.erb create mode 100755 xen/create_slice/example create mode 100755 xen/create_slice/libraries/default.rb create mode 100755 xen/create_slice/templates/default/slice_config.xen.erb create mode 100755 xen/metadata.json create mode 100755 xen/metadata.rb create mode 100644 xfs/README.rdoc create mode 100644 xfs/metadata.json create mode 100644 xfs/metadata.rb create mode 100644 xfs/recipes/default.rb create mode 100644 zsh/metadata.json create mode 100644 zsh/metadata.rb create mode 100644 zsh/recipes/default.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..781a4a8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +tmp/* +.rake_test_cache diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..37657ea --- /dev/null +++ b/NOTICE @@ -0,0 +1,15 @@ +========================= +Opscode Cookbooks Notices +========================= + +Developed at Opscode (http://www.opscode.com). + +Contributors and Copyright holders: + + * Copyright 2008-2009, Opscode + * Copyright 2008-2009, Adam Jacob + * Copyright 2008-2009, Joshua Timberman + * Copyright 2009, Joe Williams + * Copyright 2009, Joshua Sierles + * Copyright 2009, Matthew Kent + \ No newline at end of file diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..c3ad879 --- /dev/null +++ b/Rakefile @@ -0,0 +1,46 @@ +require 'rubygems' +require 'chef' +require 'json' + + +TOPDIR = File.expand_path(File.join(File.dirname(__FILE__), "..")) +TEST_CACHE = File.expand_path(File.join(TOPDIR, ".rake_test_cache")) +COMPANY_NAME = "Opscode, Inc." +SSL_EMAIL_ADDRESS = "cookbooks@opscode.com" +NEW_COOKBOOK_LICENSE = :apachev2 + +load 'chef/tasks/chef_repo.rake' +task :default => [ :test ] + +desc "Build a bootstrap.tar.gz" +task :build_bootstrap do + bootstrap_files = Rake::FileList.new + %w(apache2 runit couchdb stompserver chef passenger ruby packages).each do |cookbook| + bootstrap_files.include "#{cookbook}/**/*" + end + + tmp_dir = "tmp" + cookbooks_dir = File.join(tmp_dir, "cookbooks") + rm_rf tmp_dir + mkdir_p cookbooks_dir + bootstrap_files.each do |fn| + f = File.join(cookbooks_dir, fn) + fdir = File.dirname(f) + mkdir_p(fdir) if !File.exist?(fdir) + if File.directory?(fn) + mkdir_p(f) + else + rm_f f + safe_ln(fn, f) + end + end + + chdir(tmp_dir) do + sh %{tar zcvf bootstrap.tar.gz cookbooks} + end +end + +# remove unnecessary tasks +%w{update install roles ssl_cert}.each do |t| + Rake.application.instance_variable_get('@tasks').delete(t.to_s) +end diff --git a/activemq/README.rdoc b/activemq/README.rdoc new file mode 100644 index 0000000..6156892 --- /dev/null +++ b/activemq/README.rdoc @@ -0,0 +1,39 @@ += DESCRIPTION: + +Installs activemq and sets up a runit service. + += REQUIREMENTS: + +Tested on Ubuntu 9.04. + +Opscode cookbooks: + +* java +* runit + += ATTRIBUTES: + +* activemq[:mirror] - download URL up to the apache/activemq/apache-activemq directory. +* activemq[:version] - version to install. + += USAGE: + +Include the default recipe on systems where you want to run activemq. At this time the cookbook doesn't use any custom configuration for activemq. + += LICENSE AND AUTHOR: + +Author:: Joshua Timberman () + +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/activemq/attributes/activemq.rb b/activemq/attributes/activemq.rb new file mode 100644 index 0000000..c319462 --- /dev/null +++ b/activemq/attributes/activemq.rb @@ -0,0 +1,21 @@ +# +# Cookbook Name:: activemq +# Attributes:: activemq +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set_unless[:activemq][:mirror] = "http://mirrors.ibiblio.org/pub/mirrors" +set_unless[:activemq][:version] = "5.3.0" diff --git a/activemq/metadata.json b/activemq/metadata.json new file mode 100644 index 0000000..22ef9ed --- /dev/null +++ b/activemq/metadata.json @@ -0,0 +1,51 @@ +{ + "dependencies": { + "java": [ + + ], + "runit": [ + + ] + }, + "maintainer_email": "cookbooks@opscode.com", + "conflicting": { + + }, + "description": "Installs activemq and sets it up as a runit service", + "recipes": { + "activemq": "" + }, + "providing": { + "activemq": [ + + ] + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.2.0", + "name": "activemq", + "replacing": { + + }, + "groupings": { + + }, + "long_description": "= DESCRIPTION:\n\nInstalls activemq and sets up a runit service.\n\n= REQUIREMENTS:\n\nTested on Ubuntu 9.04.\n\nOpscode cookbooks:\n\n* java\n* runit\n\n= ATTRIBUTES:\n\n* activemq[:mirror] - download URL up to the apache/activemq/apache-activemq directory.\n* activemq[:version] - version to install.\n\n= USAGE:\n\nInclude the default recipe on systems where you want to run activemq. At this time the cookbook doesn't use any custom configuration for activemq.\n\n= LICENSE AND AUTHOR:\n\nAuthor:: Joshua Timberman ()\n\nCopyright:: 2009, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "attributes": { + + }, + "recommendations": { + + }, + "license": "Apache 2.0", + "maintainer": "Opscode, Inc.", + "suggestions": { + + } +} \ No newline at end of file diff --git a/activemq/metadata.rb b/activemq/metadata.rb new file mode 100644 index 0000000..add2bfe --- /dev/null +++ b/activemq/metadata.rb @@ -0,0 +1,14 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs activemq and sets it up as a runit service" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.2" + +%w{ubuntu debian}.each do |os| + supports os +end + +%w{java runit}.each do |cb| + depends cb +end diff --git a/activemq/recipes/default.rb b/activemq/recipes/default.rb new file mode 100644 index 0000000..b9aaff3 --- /dev/null +++ b/activemq/recipes/default.rb @@ -0,0 +1,42 @@ +# +# Cookbook Name:: activemq +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "java" + +version = node[:activemq][:version] +mirror = node[:activemq][:mirror] + +unless File.exists?("/opt/apache-activemq-#{version}/bin/activemq") + remote_file "/tmp/apache-activemq-#{version}-bin.tar.gz" do + source "#{mirror}/apache/activemq/apache-activemq/#{version}/apache-activemq-#{version}-bin.tar.gz" + mode "0644" + end + + execute "tar zxf /tmp/apache-activemq-#{version}-bin.tar.gz" do + cwd "/opt" + end +end + +file "/opt/apache-activemq-#{version}/bin/activemq" do + owner "root" + group "root" + mode "0755" +end + +runit_service "activemq" diff --git a/activemq/templates/default/sv-activemq-log-run.erb b/activemq/templates/default/sv-activemq-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/activemq/templates/default/sv-activemq-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/activemq/templates/default/sv-activemq-run.erb b/activemq/templates/default/sv-activemq-run.erb new file mode 100644 index 0000000..3982bbc --- /dev/null +++ b/activemq/templates/default/sv-activemq-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec /opt/apache-activemq-<%= @node[:activemq][:version] %>/bin/activemq diff --git a/apache2/README.rdoc b/apache2/README.rdoc new file mode 100644 index 0000000..db31d20 --- /dev/null +++ b/apache2/README.rdoc @@ -0,0 +1,78 @@ += DESCRIPTION: + +Complete Debian/Ubuntu style Apache2 configuration. + += REQUIREMENTS: + +Debian or Ubuntu preferred. + +Red Hat/CentOS and Fedora can be used but will be converted to a Debian/Ubuntu style Apache as it's far easier to manage with chef. + += ATTRIBUTES: + +The file attributes/apache.rb contains the following attribute types: + +* platform specific locations and settings. +* general settings +* prefork attributes +* worker attributes + +General settings and prefork/worker attributes are tunable. + += USAGE: + +Include the apache2 recipe to install Apache2 and get 'sane' default settings. Configuration is modularized through Apache vhost sites a la Debian style configuration. + +For Red Hat, CentOS and Fedora you should first disable selinux as it's not supported (yet), then remove the stock httpd and all it's dependencies prior to attempting to use this recipe. Many packages in these distributions drop conflicting configs into conf.d, all of which haven't been accounted for yet. Starting from scratch will also make it far easier to debug. + +== Defines: + +* apache_module: sets up an Apache module. +* apache_conf: sets up a config file for an apache module. +* apache_site: sets up a vhost site. The conf file must be available. +* web_app: copies the template for a web app and enables it as a site via apache_site. + +== Web Apps: + +Various applications that can be set up with Apache as the front end, such as PHP, Django, Rails and others can use the web_app define to set up the template and the Apache site. The define is kind of dumb, so the template needs have the application implementation settings, since we don't know what your app is or what is needed from Apache. + +We only prototype one parameter for the web_app define, "template". This is used to specify the name of the template to use in the current cookbook. When you use web_app, you can set up any parameters you want to use in your template. They will get passed to the template through the params hash. For example, the sample web_app.conf.erb template in this cookbook makes use of these. + +* docroot +* server_name +* server_aliases + +These are available as @params[:docroot], @params[:server_name], @params[:server_aliases] within the template. + +If 'cookbook' and 'template' are not specified, the current cookbook's templates/default/web_app.conf.erb will be used. If this template is not suitable for your application, copy it to your cookbook and customize as needed. + +== God Monitor: + +There's a new recipe, apache2::god_monitor. You will need to make sure to include the 'god' recipe before using the apache2::god_monitor recipe in your cookbook. + +== OpenID Auth + +Installs the mod_auth_openid module from source. Specify an array of OpenIDs that are allowed to authenticate with the attribute apache[:allowed_openids]. Use the following in a vhost to protect with OpenID authentication: + + AuthOpenIDEnabled On + AuthOpenIDDBLocation /var/cache/apache2/mod_auth_openid.db + AuthOpenIDUserProgram /usr/local/bin/mod_auth_openid.rb + +Change the DBLocation as appropriate for your platform. You'll need to change the file in the recipe to match. The UserProgram is optional if you don't want to limit access by certain OpenIDs. + += LICENSE & AUTHOR: + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/apache2/attributes/apache.rb b/apache2/attributes/apache.rb new file mode 100644 index 0000000..d6a8f76 --- /dev/null +++ b/apache2/attributes/apache.rb @@ -0,0 +1,77 @@ +# +# Cookbook Name:: apache2 +# Attributes:: apache +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Where the various parts of apache are +case platform +when "redhat","centos","fedora","suse" + set[:apache][:dir] = "/etc/httpd" + set[:apache][:log_dir] = "/var/log/httpd" + set[:apache][:user] = "apache" + set[:apache][:binary] = "/usr/sbin/httpd" + set[:apache][:icondir] = "/var/www/icons/" +when "debian","ubuntu" + set[:apache][:dir] = "/etc/apache2" + set[:apache][:log_dir] = "/var/log/apache2" + set[:apache][:user] = "www-data" + set[:apache][:binary] = "/usr/sbin/apache2" + set[:apache][:icondir] = "/usr/share/apache2/icons" +else + set[:apache][:dir] = "/etc/apache2" + set[:apache][:log_dir] = "/var/log/apache2" + set[:apache][:user] = "www-data" + set[:apache][:binary] = "/usr/sbin/apache2" + set[:apache][:icondir] = "/usr/share/apache2/icons" +end + +### +# These settings need the unless, since we want them to be tunable, +# and we don't want to override the tunings. +### + +# General settings +set_unless[:apache][:listen_ports] = [ "80","443" ] +set_unless[:apache][:contact] = "ops@example.com" +set_unless[:apache][:timeout] = 300 +set_unless[:apache][:keepalive] = "On" +set_unless[:apache][:keepaliverequests] = 100 +set_unless[:apache][:keepalivetimeout] = 5 + +# Security +set_unless[:apache][:servertokens] = "Prod" +set_unless[:apache][:serversignature] = "On" +set_unless[:apache][:traceenable] = "On" + +# mod_auth_openids +set_unless[:apache][:allowed_openids] = Array.new + +# Prefork Attributes +set_unless[:apache][:prefork][:startservers] = 16 +set_unless[:apache][:prefork][:minspareservers] = 16 +set_unless[:apache][:prefork][:maxspareservers] = 32 +set_unless[:apache][:prefork][:serverlimit] = 400 +set_unless[:apache][:prefork][:maxclients] = 400 +set_unless[:apache][:prefork][:maxrequestsperchild] = 10000 + +# Worker Attributes +set_unless[:apache][:worker][:startservers] = 4 +set_unless[:apache][:worker][:maxclients] = 1024 +set_unless[:apache][:worker][:minsparethreads] = 64 +set_unless[:apache][:worker][:maxsparethreads] = 192 +set_unless[:apache][:worker][:threadsperchild] = 64 +set_unless[:apache][:worker][:maxrequestsperchild] = 0 diff --git a/apache2/definitions/apache_conf.rb b/apache2/definitions/apache_conf.rb new file mode 100644 index 0000000..2439289 --- /dev/null +++ b/apache2/definitions/apache_conf.rb @@ -0,0 +1,25 @@ +# +# Cookbook Name:: apache2 +# Definition:: apache_conf +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :apache_conf do + template "#{node[:apache][:dir]}/mods-available/#{params[:name]}.conf" do + source "mods/#{params[:name]}.conf.erb" + notifies :restart, resources(:service => "apache2") + end +end diff --git a/apache2/definitions/apache_module.rb b/apache2/definitions/apache_module.rb new file mode 100644 index 0000000..2bd739e --- /dev/null +++ b/apache2/definitions/apache_module.rb @@ -0,0 +1,43 @@ +# +# Cookbook Name:: apache2 +# Definition:: apache_module +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :apache_module, :enable => true, :conf => false do + include_recipe "apache2" + + if params[:conf] + apache_conf params[:name] + end + + if params[:enable] + execute "a2enmod #{params[:name]}" do + command "/usr/sbin/a2enmod #{params[:name]}" + notifies :restart, resources(:service => "apache2") + not_if do (File.symlink?("#{node[:apache][:dir]}/mods-enabled/#{params[:name]}.load") and + ((File.exists?("#{node[:apache][:dir]}/mods-available/#{params[:name]}.conf"))? + (File.symlink?("#{node[:apache][:dir]}/mods-enabled/#{params[:name]}.conf")):(true))) + end + end + else + execute "a2dismod #{params[:name]}" do + command "/usr/sbin/a2dismod #{params[:name]}" + notifies :restart, resources(:service => "apache2") + only_if do File.symlink?("#{node[:apache][:dir]}/mods-enabled/#{params[:name]}.load") end + end + end +end diff --git a/apache2/definitions/apache_site.rb b/apache2/definitions/apache_site.rb new file mode 100644 index 0000000..7316e01 --- /dev/null +++ b/apache2/definitions/apache_site.rb @@ -0,0 +1,40 @@ +# +# Cookbook Name:: apache2 +# Definition:: apache_site +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :apache_site, :enable => true do + include_recipe "apache2" + + if params[:enable] + execute "a2ensite #{params[:name]}" do + command "/usr/sbin/a2ensite #{params[:name]}" + notifies :restart, resources(:service => "apache2") + not_if do + File.symlink?("#{node[:apache][:dir]}/sites-enabled/#{params[:name]}") or + File.symlink?("#{node[:apache][:dir]}/sites-enabled/000-#{params[:name]}") + end + only_if do File.exists?("#{node[:apache][:dir]}/sites-available/#{params[:name]}") end + end + else + execute "a2dissite #{params[:name]}" do + command "/usr/sbin/a2dissite #{params[:name]}" + notifies :restart, resources(:service => "apache2") + only_if do File.symlink?("#{node[:apache][:dir]}/sites-enabled/#{params[:name]}") end + end + end +end diff --git a/apache2/definitions/web_app.rb b/apache2/definitions/web_app.rb new file mode 100644 index 0000000..b532708 --- /dev/null +++ b/apache2/definitions/web_app.rb @@ -0,0 +1,49 @@ +# +# Cookbook Name:: apache2 +# Definition:: web_app +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :web_app, :template => "web_app.conf.erb" do + + application_name = params[:name] + + include_recipe "apache2" + include_recipe "apache2::mod_rewrite" + include_recipe "apache2::mod_deflate" + include_recipe "apache2::mod_headers" + + template "#{node[:apache][:dir]}/sites-available/#{application_name}.conf" do + source params[:template] + owner "root" + group "root" + mode 0644 + if params[:cookbook] + cookbook params[:cookbook] + end + variables( + :application_name => application_name, + :params => params + ) + if File.exists?("#{node[:apache][:dir]}/sites-enabled/#{application_name}.conf") + notifies :reload, resources(:service => "apache2"), :delayed + end + end + + apache_site "#{params[:name]}.conf" do + enable enable_setting + end +end diff --git a/apache2/files/default/apache2_module_conf_generate.pl b/apache2/files/default/apache2_module_conf_generate.pl new file mode 100644 index 0000000..83f849e --- /dev/null +++ b/apache2/files/default/apache2_module_conf_generate.pl @@ -0,0 +1,41 @@ +#!/usr/bin/perl + +=begin + +Generates Ubuntu style module.load files. + +./apache2_module_conf_generate.pl /usr/lib64/httpd/modules /etc/httpd/mods-available + +ARGV[0] is the apache modules directory, ARGV[1] is where you want 'em. + +=cut + +use File::Find; + +use strict; +use warnings; + +die "Must have '/path/to/modules' and '/path/to/modules.load'" + unless $ARGV[0] && $ARGV[1]; + +find( + { + wanted => sub { + return 1 if $File::Find::name !~ /\.so$/; + my $modfile = $_; + $modfile =~ /(lib|mod_)(.+)\.so$/; + my $modname = $2; + my $filename = "$ARGV[1]/$modname.load"; + unless ( -f $filename ) { + open( FILE, ">", $filename ) or die "Cannot open $filename"; + print FILE "LoadModule " . $modname . "_module $File::Find::name\n"; + close(FILE); + } + }, + follow => 1, + }, + $ARGV[0] +); + +exit 0; + diff --git a/apache2/metadata.json b/apache2/metadata.json new file mode 100644 index 0000000..ad20aee --- /dev/null +++ b/apache2/metadata.json @@ -0,0 +1,523 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs and configures all aspects of apache2 using Debian style symlinks with helper definitions", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "apache2::mod_python": "Apache module 'python'", + "apache2::mod_authn_file": "Apache module 'authn_file'", + "apache2::mod_dir": "Apache module 'dir' with config file", + "apache2::mod_php5": "Apache module 'php5'", + "apache2::mod_proxy_balancer": "Apache module 'proxy_balancer'", + "apache2::mod_dav": "Apache module 'dav'", + "apache2::mod_authz_groupfile": "Apache module 'authz_groupfile'", + "apache2::mod_auth_basic": "Apache module 'auth_basic'", + "apache2::mod_setenvif": "Apache module 'setenvif' with config file", + "apache2::mod_authz_user": "Apache module 'authz_user'", + "apache2::mod_deflate": "Apache module 'deflate' with config file", + "apache2::mod_ssl": "Apache module 'ssl' with config file, adds port 443 to listen_ports", + "apache2::mod_negotiation": "Apache module 'negotiation' with config file", + "apache2::mod_dav_svn": "Apache module 'dav_svn'", + "apache2::mod_authz_host": "Apache module 'authz_host'", + "apache2::mod_rewrite": "Apache module 'rewrite'", + "apache2::mod_cgi": "Apache module 'cgi'", + "apache2::mod_fcgid": "Apache module 'fcgid', package on ubuntu\/debian, rhel\/centos, compile source on suse; with config file", + "apache2::mod_auth_digest": "Apache module 'auth_digest'", + "apache2::mod_env": "Apache module 'env'", + "apache2::mod_headers": "Apache module 'headers'", + "apache2::mod_autoindex": "Apache module 'autoindex' with config file", + "apache2::mod_authnz_ldap": "Apache module 'authnz_ldap'", + "apache2::mod_proxy_connect": "Apache module 'proxy_connect'", + "apache2::mod_proxy": "Apache module 'proxy' with config file", + "apache2": "Main Apache configuration", + "apache2::mod_alias": "Apache module 'alias' with config file", + "apache2::mod_status": "Apache module 'status' with config file", + "apache2::mod_ldap": "Apache module 'ldap'", + "apache2::mod_authz_default": "Apache module 'authz_default'", + "apache2::mod_log_config": "Apache module 'log_config'", + "apache2::mod_expires": "Apache module 'expires'", + "apache2::god_monitor": "", + "apache2::mod_proxy_http": "Apache module 'proxy_http'", + "apache2::mod_auth_openid": "Apache module 'authopenid'", + "apache2::mod_mime": "Apache module 'mime' with config file", + "apache2::mod_proxy_ajp": "Apache module 'proxy_ajp'", + "apache2": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "centos": [ + + ], + "debian": [ + + ], + "redhat": [ + + ] + }, + "version": "0.10.0", + "name": "apache2", + "conflicting": { + + }, + "attributes": { + "apache\/traceenable": { + "default": "On", + "type": "string", + "multiple_values": false, + "description": "Determine behavior of TRACE requests", + "display_name": "Apache Trace Enable", + "recipes": [ + + ], + "required": false + }, + "apache\/timeout": { + "default": "300", + "type": "string", + "multiple_values": false, + "description": "Connection timeout value", + "display_name": "Apache Timeout", + "recipes": [ + + ], + "required": false + }, + "apache\/icondir": { + "default": "\/usr\/share\/apache2\/icons", + "type": "string", + "multiple_values": false, + "description": "Directory location for icons", + "display_name": "Apache Icondir", + "recipes": [ + + ], + "required": false + }, + "apache\/user": { + "default": "www-data", + "type": "string", + "multiple_values": false, + "description": "User Apache runs as", + "display_name": "Apache User", + "recipes": [ + + ], + "required": false + }, + "apache\/worker\/threadsperchild": { + "default": "64", + "type": "string", + "multiple_values": false, + "description": "Constant number of worker threads in each server process", + "display_name": "Apache Worker MPM ThreadsPerChild", + "recipes": [ + + ], + "required": false + }, + "apache\/worker\/maxclients": { + "default": "1024", + "type": "string", + "multiple_values": false, + "description": "Maximum number of simultaneous connections", + "display_name": "Apache Worker MPM MaxClients", + "recipes": [ + + ], + "required": false + }, + "apache\/worker": { + "type": "hash", + "multiple_values": false, + "description": "Hash of Apache prefork tuning attributes.", + "display_name": "Apache Worker", + "recipes": [ + + ], + "required": false + }, + "apache\/contact": { + "default": "ops@example.com", + "type": "string", + "multiple_values": false, + "description": "Email address of webmaster", + "display_name": "Apache Contact", + "recipes": [ + + ], + "required": false + }, + "apache\/prefork\/startservers": { + "default": "16", + "type": "string", + "multiple_values": false, + "description": "Number of MPM servers to start", + "display_name": "Apache Prefork MPM StartServers", + "recipes": [ + + ], + "required": false + }, + "apache\/prefork\/minspareservers": { + "default": "16", + "type": "string", + "multiple_values": false, + "description": "Minimum number of spare server processes", + "display_name": "Apache Prefork MPM MinSpareServers", + "recipes": [ + + ], + "required": false + }, + "apache\/allowed_openids": { + "default": "", + "type": "string", + "multiple_values": false, + "description": "Array of OpenIDs allowed to authenticate", + "display_name": "Apache Allowed OpenIDs", + "recipes": [ + + ], + "required": false + }, + "apache\/keepalivetimeout": { + "default": "5", + "type": "string", + "multiple_values": false, + "description": "Time to wait for requests on persistent connection", + "display_name": "Apache Keepalive Timeout", + "recipes": [ + + ], + "required": false + }, + "apache\/keepaliverequests": { + "default": "100", + "type": "string", + "multiple_values": false, + "description": "Number of requests allowed on a persistent connection", + "display_name": "Apache Keepalive Requests", + "recipes": [ + + ], + "required": false + }, + "apache\/worker\/maxrequestsperchild": { + "default": "0", + "type": "string", + "multiple_values": false, + "description": "Maximum number of request a child process will handle", + "display_name": "Apache Worker MPM MaxRequestsPerChild", + "recipes": [ + + ], + "required": false + }, + "apache\/listen_ports": { + "default": [ + "80", + "443" + ], + "type": "array", + "multiple_values": false, + "description": "Ports that Apache should listen on", + "display_name": "Apache Listen Ports", + "recipes": [ + + ], + "required": false + }, + "apache\/dir": { + "default": "\/etc\/apache2", + "type": "string", + "multiple_values": false, + "description": "Location for Apache configuration", + "display_name": "Apache Directory", + "recipes": [ + + ], + "required": false + }, + "apache\/worker\/maxsparethreads": { + "default": "192", + "type": "string", + "multiple_values": false, + "description": "Maximum number of spare worker threads", + "display_name": "Apache Worker MPM MaxSpareThreads", + "recipes": [ + + ], + "required": false + }, + "apache\/prefork\/maxrequestsperchild": { + "default": "10000", + "type": "string", + "multiple_values": false, + "description": "Maximum number of request a child process will handle", + "display_name": "Apache Prefork MPM MaxRequestsPerChild", + "recipes": [ + + ], + "required": false + }, + "apache\/prefork\/serverlimit": { + "default": "400", + "type": "string", + "multiple_values": false, + "description": "Upper limit on configurable server processes", + "display_name": "Apache Prefork MPM ServerLimit", + "recipes": [ + + ], + "required": false + }, + "apache\/binary": { + "default": "\/usr\/sbin\/apache2", + "type": "string", + "multiple_values": false, + "description": "Apache server daemon program", + "display_name": "Apache Binary", + "recipes": [ + + ], + "required": false + }, + "apache\/prefork\/maxspareservers": { + "default": "32", + "type": "string", + "multiple_values": false, + "description": "Maximum number of spare server processes", + "display_name": "Apache Prefork MPM MaxSpareServers", + "recipes": [ + + ], + "required": false + }, + "apache\/keepalive": { + "default": "On", + "type": "string", + "multiple_values": false, + "description": "HTTP persistent connections", + "display_name": "Apache Keepalive", + "recipes": [ + + ], + "required": false + }, + "apache": { + "type": "hash", + "multiple_values": false, + "description": "Hash of Apache attributes", + "display_name": "Apache Hash", + "recipes": [ + + ], + "required": false + }, + "apache\/worker\/startservers": { + "default": "4", + "type": "string", + "multiple_values": false, + "description": "Initial number of server processes to start", + "display_name": "Apache Worker MPM StartServers", + "recipes": [ + + ], + "required": false + }, + "apache\/prefork\/maxclients": { + "default": "400", + "type": "string", + "multiple_values": false, + "description": "Maximum number of simultaneous connections", + "display_name": "Apache Prefork MPM MaxClients", + "recipes": [ + + ], + "required": false + }, + "apache\/prefork": { + "type": "hash", + "multiple_values": false, + "description": "Hash of Apache prefork tuning attributes.", + "display_name": "Apache Prefork", + "recipes": [ + + ], + "required": false + }, + "apache\/servertokens": { + "default": "Prod", + "type": "string", + "multiple_values": false, + "description": "Server response header", + "display_name": "Apache Server Tokens", + "recipes": [ + + ], + "required": false + }, + "apache\/worker\/minsparethreads": { + "default": "64", + "type": "string", + "multiple_values": false, + "description": "Minimum number of spare worker threads", + "display_name": "Apache Worker MPM MinSpareThreads", + "recipes": [ + + ], + "required": false + }, + "apache\/serversignature": { + "default": "On", + "type": "string", + "multiple_values": false, + "description": "Configure footer on server-generated documents", + "display_name": "Apache Server Signature", + "recipes": [ + + ], + "required": false + }, + "apache\/log_dir": { + "default": "\/etc\/apache2", + "type": "string", + "multiple_values": false, + "description": "Location for Apache logs", + "display_name": "Apache Log Directory", + "recipes": [ + + ], + "required": false + } + }, + "providing": { + "apache2::mod_python": [ + + ], + "apache2::mod_authn_file": [ + + ], + "apache2::mod_dir": [ + + ], + "apache2::mod_php5": [ + + ], + "apache2::mod_proxy_balancer": [ + + ], + "apache2::mod_dav": [ + + ], + "apache2::mod_authz_groupfile": [ + + ], + "apache2::mod_auth_basic": [ + + ], + "apache2::mod_setenvif": [ + + ], + "apache2::mod_authz_user": [ + + ], + "apache2::mod_deflate": [ + + ], + "apache2::mod_ssl": [ + + ], + "apache2::mod_negotiation": [ + + ], + "apache2::mod_dav_svn": [ + + ], + "apache2::mod_authz_host": [ + + ], + "apache2::mod_rewrite": [ + + ], + "apache2::mod_cgi": [ + + ], + "apache2::mod_fcgid": [ + + ], + "apache2::mod_auth_digest": [ + + ], + "apache2::mod_env": [ + + ], + "apache2::mod_headers": [ + + ], + "apache2::mod_autoindex": [ + + ], + "apache2::mod_authnz_ldap": [ + + ], + "apache2::mod_proxy_connect": [ + + ], + "apache2::mod_proxy": [ + + ], + "apache2::mod_alias": [ + + ], + "apache2::mod_status": [ + + ], + "apache2::mod_ldap": [ + + ], + "apache2::mod_authz_default": [ + + ], + "apache2::mod_log_config": [ + + ], + "apache2": [ + + ], + "apache2::mod_expires": [ + + ], + "apache2::god_monitor": [ + + ], + "apache2::mod_proxy_http": [ + + ], + "apache2::mod_auth_openid": [ + + ], + "apache2::mod_mime": [ + + ], + "apache2::mod_proxy_ajp": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nComplete Debian\/Ubuntu style Apache2 configuration.\n\n= REQUIREMENTS:\n\nDebian or Ubuntu preferred.\n\nRed Hat\/CentOS and Fedora can be used but will be converted to a Debian\/Ubuntu style Apache as it's far easier to manage with chef. \n\n= ATTRIBUTES:\n\nThe file attributes\/apache.rb contains the following attribute types:\n\n* platform specific locations and settings.\n* general settings\n* prefork attributes\n* worker attributes\n\nGeneral settings and prefork\/worker attributes are tunable.\n\n= USAGE:\n\nInclude the apache2 recipe to install Apache2 and get 'sane' default settings. Configuration is modularized through Apache vhost sites a la Debian style configuration.\n\nFor Red Hat, CentOS and Fedora you should first disable selinux as it's not supported (yet), then remove the stock httpd and all it's dependencies prior to attempting to use this recipe. Many packages in these distributions drop conflicting configs into conf.d, all of which haven't been accounted for yet. Starting from scratch will also make it far easier to debug.\n\n== Defines:\n\n* apache_module: sets up an Apache module.\n* apache_conf: sets up a config file for an apache module.\n* apache_site: sets up a vhost site. The conf file must be available.\n* web_app: copies the template for a web app and enables it as a site via apache_site.\n\n== Web Apps:\n\nVarious applications that can be set up with Apache as the front end, such as PHP, Django, Rails and others can use the web_app define to set up the template and the Apache site. The define is kind of dumb, so the template needs have the application implementation settings, since we don't know what your app is or what is needed from Apache.\n\nWe only prototype one parameter for the web_app define, \"template\". This is used to specify the name of the template to use in the current cookbook. When you use web_app, you can set up any parameters you want to use in your template. They will get passed to the template through the params hash. For example, the sample web_app.conf.erb template in this cookbook makes use of these.\n\n* docroot\n* server_name\n* server_aliases\n\nThese are available as @params[:docroot], @params[:server_name], @params[:server_aliases] within the template. \n\nIf 'cookbook' and 'template' are not specified, the current cookbook's templates\/default\/web_app.conf.erb will be used. If this template is not suitable for your application, copy it to your cookbook and customize as needed.\n\n== God Monitor:\n\nThere's a new recipe, apache2::god_monitor. You will need to make sure to include the 'god' recipe before using the apache2::god_monitor recipe in your cookbook.\n\n== OpenID Auth\n\nInstalls the mod_auth_openid module from source. Specify an array of OpenIDs that are allowed to authenticate with the attribute apache[:allowed_openids]. Use the following in a vhost to protect with OpenID authentication:\n\n AuthOpenIDEnabled On\n AuthOpenIDDBLocation \/var\/cache\/apache2\/mod_auth_openid.db\n AuthOpenIDUserProgram \/usr\/local\/bin\/mod_auth_openid.rb\n\nChange the DBLocation as appropriate for your platform. You'll need to change the file in the recipe to match. The UserProgram is optional if you don't want to limit access by certain OpenIDs.\n\n= LICENSE & AUTHOR:\n\nAuthor:: Joshua Timberman ()\nCopyright:: 2009, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/apache2/metadata.rb b/apache2/metadata.rb new file mode 100644 index 0000000..ea9ab9f --- /dev/null +++ b/apache2/metadata.rb @@ -0,0 +1,197 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures all aspects of apache2 using Debian style symlinks with helper definitions" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.10.0" +recipe "apache2", "Main Apache configuration" +recipe "apache2::mod_alias", "Apache module 'alias' with config file" +recipe "apache2::mod_auth_basic", "Apache module 'auth_basic'" +recipe "apache2::mod_auth_digest", "Apache module 'auth_digest'" +recipe "apache2::mod_auth_openid", "Apache module 'authopenid'" +recipe "apache2::mod_authn_file", "Apache module 'authn_file'" +recipe "apache2::mod_authnz_ldap", "Apache module 'authnz_ldap'" +recipe "apache2::mod_authz_default", "Apache module 'authz_default'" +recipe "apache2::mod_authz_groupfile", "Apache module 'authz_groupfile'" +recipe "apache2::mod_authz_host", "Apache module 'authz_host'" +recipe "apache2::mod_authz_user", "Apache module 'authz_user'" +recipe "apache2::mod_autoindex", "Apache module 'autoindex' with config file" +recipe "apache2::mod_cgi", "Apache module 'cgi'" +recipe "apache2::mod_dav", "Apache module 'dav'" +recipe "apache2::mod_dav_svn", "Apache module 'dav_svn'" +recipe "apache2::mod_deflate", "Apache module 'deflate' with config file" +recipe "apache2::mod_dir", "Apache module 'dir' with config file" +recipe "apache2::mod_env", "Apache module 'env'" +recipe "apache2::mod_expires", "Apache module 'expires'" +recipe "apache2::mod_fcgid", "Apache module 'fcgid', package on ubuntu/debian, rhel/centos, compile source on suse; with config file" +recipe "apache2::mod_headers", "Apache module 'headers'" +recipe "apache2::mod_ldap", "Apache module 'ldap'" +recipe "apache2::mod_log_config", "Apache module 'log_config'" +recipe "apache2::mod_mime", "Apache module 'mime' with config file" +recipe "apache2::mod_negotiation", "Apache module 'negotiation' with config file" +recipe "apache2::mod_php5", "Apache module 'php5'" +recipe "apache2::mod_proxy", "Apache module 'proxy' with config file" +recipe "apache2::mod_proxy_ajp", "Apache module 'proxy_ajp'" +recipe "apache2::mod_proxy_balancer", "Apache module 'proxy_balancer'" +recipe "apache2::mod_proxy_connect", "Apache module 'proxy_connect'" +recipe "apache2::mod_proxy_http", "Apache module 'proxy_http'" +recipe "apache2::mod_python", "Apache module 'python'" +recipe "apache2::mod_rewrite", "Apache module 'rewrite'" +recipe "apache2::mod_setenvif", "Apache module 'setenvif' with config file" +recipe "apache2::mod_ssl", "Apache module 'ssl' with config file, adds port 443 to listen_ports" +recipe "apache2::mod_status", "Apache module 'status' with config file" + +%w{redhat centos debian ubuntu}.each do |os| + supports os +end + +attribute "apache", + :display_name => "Apache Hash", + :description => "Hash of Apache attributes", + :type => "hash" + +attribute "apache/dir", + :display_name => "Apache Directory", + :description => "Location for Apache configuration", + :default => "/etc/apache2" + +attribute "apache/log_dir", + :display_name => "Apache Log Directory", + :description => "Location for Apache logs", + :default => "/etc/apache2" + +attribute "apache/user", + :display_name => "Apache User", + :description => "User Apache runs as", + :default => "www-data" + +attribute "apache/binary", + :display_name => "Apache Binary", + :description => "Apache server daemon program", + :default => "/usr/sbin/apache2" + +attribute "apache/icondir", + :display_name => "Apache Icondir", + :description => "Directory location for icons", + :default => "/usr/share/apache2/icons" + +attribute "apache/listen_ports", + :display_name => "Apache Listen Ports", + :description => "Ports that Apache should listen on", + :type => "array", + :default => [ "80", "443" ] + +attribute "apache/contact", + :display_name => "Apache Contact", + :description => "Email address of webmaster", + :default => "ops@example.com" + +attribute "apache/timeout", + :display_name => "Apache Timeout", + :description => "Connection timeout value", + :default => "300" + +attribute "apache/keepalive", + :display_name => "Apache Keepalive", + :description => "HTTP persistent connections", + :default => "On" + +attribute "apache/keepaliverequests", + :display_name => "Apache Keepalive Requests", + :description => "Number of requests allowed on a persistent connection", + :default => "100" + +attribute "apache/keepalivetimeout", + :display_name => "Apache Keepalive Timeout", + :description => "Time to wait for requests on persistent connection", + :default => "5" + +attribute "apache/servertokens", + :display_name => "Apache Server Tokens", + :description => "Server response header", + :default => "Prod" + +attribute "apache/serversignature", + :display_name => "Apache Server Signature", + :description => "Configure footer on server-generated documents", + :default => "On" + +attribute "apache/traceenable", + :display_name => "Apache Trace Enable", + :description => "Determine behavior of TRACE requests", + :default => "On" + +attribute "apache/allowed_openids", + :display_name => "Apache Allowed OpenIDs", + :description => "Array of OpenIDs allowed to authenticate", + :default => "" + +attribute "apache/prefork", + :display_name => "Apache Prefork", + :description => "Hash of Apache prefork tuning attributes.", + :type => "hash" + +attribute "apache/prefork/startservers", + :display_name => "Apache Prefork MPM StartServers", + :description => "Number of MPM servers to start", + :default => "16" + +attribute "apache/prefork/minspareservers", + :display_name => "Apache Prefork MPM MinSpareServers", + :description => "Minimum number of spare server processes", + :default => "16" + +attribute "apache/prefork/maxspareservers", + :display_name => "Apache Prefork MPM MaxSpareServers", + :description => "Maximum number of spare server processes", + :default => "32" + +attribute "apache/prefork/serverlimit", + :display_name => "Apache Prefork MPM ServerLimit", + :description => "Upper limit on configurable server processes", + :default => "400" + +attribute "apache/prefork/maxclients", + :display_name => "Apache Prefork MPM MaxClients", + :description => "Maximum number of simultaneous connections", + :default => "400" + +attribute "apache/prefork/maxrequestsperchild", + :display_name => "Apache Prefork MPM MaxRequestsPerChild", + :description => "Maximum number of request a child process will handle", + :default => "10000" + +attribute "apache/worker", + :display_name => "Apache Worker", + :description => "Hash of Apache prefork tuning attributes.", + :type => "hash" + +attribute "apache/worker/startservers", + :display_name => "Apache Worker MPM StartServers", + :description => "Initial number of server processes to start", + :default => "4" + +attribute "apache/worker/maxclients", + :display_name => "Apache Worker MPM MaxClients", + :description => "Maximum number of simultaneous connections", + :default => "1024" + +attribute "apache/worker/minsparethreads", + :display_name => "Apache Worker MPM MinSpareThreads", + :description => "Minimum number of spare worker threads", + :default => "64" + +attribute "apache/worker/maxsparethreads", + :display_name => "Apache Worker MPM MaxSpareThreads", + :description => "Maximum number of spare worker threads", + :default => "192" + +attribute "apache/worker/threadsperchild", + :display_name => "Apache Worker MPM ThreadsPerChild", + :description => "Constant number of worker threads in each server process", + :default => "64" + +attribute "apache/worker/maxrequestsperchild", + :display_name => "Apache Worker MPM MaxRequestsPerChild", + :description => "Maximum number of request a child process will handle", + :default => "0" diff --git a/apache2/recipes/default.rb b/apache2/recipes/default.rb new file mode 100644 index 0000000..2c42933 --- /dev/null +++ b/apache2/recipes/default.rb @@ -0,0 +1,191 @@ +# +# Cookbook Name:: apache2 +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "apache2" do + case node[:platform] + when "centos","redhat","fedora","suse" + package_name "httpd" + when "debian","ubuntu" + package_name "apache2" + end + action :install +end + +service "apache2" do + case node[:platform] + when "centos","redhat","fedora","suse" + service_name "httpd" + # If restarted/reloaded too quickly httpd has a habit of failing. + # This may happen with multiple recipes notifying apache to restart - like + # during the initial bootstrap. + restart_command "/sbin/service httpd restart && sleep 1" + reload_command "/sbin/service httpd reload && sleep 1" + when "debian","ubuntu" + service_name "apache2" + end + supports value_for_platform( + "debian" => { "4.0" => [ :restart, :reload ], "default" => [ :restart, :reload, :status ] }, + "ubuntu" => { "default" => [ :restart, :reload, :status ] }, + "centos" => { "default" => [ :restart, :reload, :status ] }, + "redhat" => { "default" => [ :restart, :reload, :status ] }, + "fedora" => { "default" => [ :restart, :reload, :status ] }, + "default" => { "default" => [:restart, :reload ] } + ) + action :enable +end + +if platform?("centos", "redhat", "fedora", "suse") + directory node[:apache][:log_dir] do + mode 0755 + action :create + end + + remote_file "/usr/local/bin/apache2_module_conf_generate.pl" do + source "apache2_module_conf_generate.pl" + mode 0755 + owner "root" + group "root" + end + + %w{sites-available sites-enabled mods-available mods-enabled}.each do |dir| + directory "#{node[:apache][:dir]}/#{dir}" do + mode 0755 + owner "root" + group "root" + action :create + end + end + + execute "generate-module-list" do + if node[:kernel][:machine] == "x86_64" + libdir = "lib64" + else + libdir = "lib" + end + command "/usr/local/bin/apache2_module_conf_generate.pl /usr/#{libdir}/httpd/modules /etc/httpd/mods-available" + + action :run + end + + %w{a2ensite a2dissite a2enmod a2dismod}.each do |modscript| + template "/usr/sbin/#{modscript}" do + source "#{modscript}.erb" + mode 0755 + owner "root" + group "root" + end + end + + # installed by default on centos/rhel, remove in favour of mods-enabled + file "#{node[:apache][:dir]}/conf.d/proxy_ajp.conf" do + action :delete + backup false + end + file "#{node[:apache][:dir]}/conf.d/README" do + action :delete + backup false + end + + # welcome page moved to the default-site.rb temlate + file "#{node[:apache][:dir]}/conf.d/welcome.conf" do + action :delete + backup false + end +end + +directory "#{node[:apache][:dir]}/ssl" do + action :create + mode 0755 + owner "root" + group "root" +end + +template "apache2.conf" do + case node[:platform] + when "centos","redhat","fedora" + path "#{node[:apache][:dir]}/conf/httpd.conf" + when "debian","ubuntu" + path "#{node[:apache][:dir]}/apache2.conf" + end + source "apache2.conf.erb" + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "apache2") +end + +template "security" do + path "#{node[:apache][:dir]}/conf.d/security" + source "security.erb" + owner "root" + group "root" + mode 0644 + backup false + notifies :restart, resources(:service => "apache2") +end + +template "charset" do + path "#{node[:apache][:dir]}/conf.d/charset" + source "charset.erb" + owner "root" + group "root" + mode 0644 + backup false + notifies :restart, resources(:service => "apache2") +end + +template "#{node[:apache][:dir]}/ports.conf" do + source "ports.conf.erb" + group "root" + owner "root" + variables :apache_listen_ports => node[:apache][:listen_ports] + mode 0644 + notifies :restart, resources(:service => "apache2") +end + +template "#{node[:apache][:dir]}/sites-available/default" do + source "default-site.erb" + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "apache2") +end + +include_recipe "apache2::mod_status" +include_recipe "apache2::mod_alias" +include_recipe "apache2::mod_auth_basic" +include_recipe "apache2::mod_authn_file" +include_recipe "apache2::mod_authz_default" +include_recipe "apache2::mod_authz_groupfile" +include_recipe "apache2::mod_authz_host" +include_recipe "apache2::mod_authz_user" +include_recipe "apache2::mod_autoindex" +include_recipe "apache2::mod_dir" +include_recipe "apache2::mod_env" +include_recipe "apache2::mod_mime" +include_recipe "apache2::mod_negotiation" +include_recipe "apache2::mod_setenvif" +include_recipe "apache2::mod_log_config" if platform?("centos", "redhat", "suse") + +# uncomment to get working example site on centos/redhat/fedora +#apache_site "default" + +service "apache2" do + action :start +end diff --git a/apache2/recipes/god_monitor.rb b/apache2/recipes/god_monitor.rb new file mode 100644 index 0000000..b3c49eb --- /dev/null +++ b/apache2/recipes/god_monitor.rb @@ -0,0 +1,33 @@ +# +# Cookbook Name:: apache2 +# Recipe:: god_monitor +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_service = service "apache2" do + action :nothing +end + +start_command = apache_service.start_command +stop_command = apache_service.stop_command +restart_command = apache_service.restart_command + +god_monitor "apache2" do + config "apache2.god.erb" + start (start_command)?start_command : "/etc/init.d/#{apache_service.service_name} start" + restart (restart_command)?restart_command : "/etc/init.d/#{apache_service.service_name} restart" + stop (stop_command)?stop_command : "/etc/init.d/#{apache_service.service_name} stop" +end diff --git a/apache2/recipes/mod_alias.rb b/apache2/recipes/mod_alias.rb new file mode 100644 index 0000000..a4618ed --- /dev/null +++ b/apache2/recipes/mod_alias.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: alias +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "alias" do + conf true +end diff --git a/apache2/recipes/mod_auth_basic.rb b/apache2/recipes/mod_auth_basic.rb new file mode 100644 index 0000000..d30264f --- /dev/null +++ b/apache2/recipes/mod_auth_basic.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: auth_basic +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "auth_basic" diff --git a/apache2/recipes/mod_auth_digest.rb b/apache2/recipes/mod_auth_digest.rb new file mode 100644 index 0000000..5aef926 --- /dev/null +++ b/apache2/recipes/mod_auth_digest.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: auth_digest +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "auth_digest" diff --git a/apache2/recipes/mod_auth_openid.rb b/apache2/recipes/mod_auth_openid.rb new file mode 100644 index 0000000..83cc220 --- /dev/null +++ b/apache2/recipes/mod_auth_openid.rb @@ -0,0 +1,59 @@ +# +# Cookbook Name:: apache2 +# Recipe:: mod_auth_openid +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +%w{ apache2-prefork-dev libopkele-dev libopkele3 }.each do |pkg| + package pkg +end + +remote_file "/tmp/mod_auth_openid-0.4.tar.gz" do + source "http://butterfat.net/releases/mod_auth_openid/mod_auth_openid-0.4.tar.gz" + mode 0644 +end + +bash "install mod_auth_openid" do + cwd "/tmp" + code <<-EOH + tar zxvf mod_auth_openid-0.4.tar.gz + cd mod_auth_openid-0.4 && ./configure + perl -pi -e "s/-i -a -n 'authopenid'/-i -n 'authopenid'/g" Makefile + make && make install + EOH + not_if { File.exists?("/usr/lib/apache2/modules/mod_auth_openid.so") } +end + +file "/var/cache/apache2/mod_auth_openid.db" do + owner node[:apache][:user] + mode 0600 +end + +template "#{node[:apache][:dir]}/mods-available/authopenid.load" do + source "mods/authopenid.load.erb" + owner "root" + group "root" + mode 0644 +end + +apache_module "authopenid" + +template "/usr/local/bin/mod_auth_openid.rb" do + source "mod_auth_openid.rb.erb" + owner node[:apache][:user] + group node[:apache][:user] + mode 0750 +end diff --git a/apache2/recipes/mod_authn_file.rb b/apache2/recipes/mod_authn_file.rb new file mode 100644 index 0000000..872caa7 --- /dev/null +++ b/apache2/recipes/mod_authn_file.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: authn_file +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "authn_file" diff --git a/apache2/recipes/mod_authnz_ldap.rb b/apache2/recipes/mod_authnz_ldap.rb new file mode 100644 index 0000000..0310d24 --- /dev/null +++ b/apache2/recipes/mod_authnz_ldap.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: authnz_ldap +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "authnz_ldap" diff --git a/apache2/recipes/mod_authz_default.rb b/apache2/recipes/mod_authz_default.rb new file mode 100644 index 0000000..123536d --- /dev/null +++ b/apache2/recipes/mod_authz_default.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: authz_default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "authz_default" diff --git a/apache2/recipes/mod_authz_groupfile.rb b/apache2/recipes/mod_authz_groupfile.rb new file mode 100644 index 0000000..b2833b2 --- /dev/null +++ b/apache2/recipes/mod_authz_groupfile.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: authz_groupfile +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "authz_groupfile" diff --git a/apache2/recipes/mod_authz_host.rb b/apache2/recipes/mod_authz_host.rb new file mode 100644 index 0000000..87c1a4b --- /dev/null +++ b/apache2/recipes/mod_authz_host.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: authz_host +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "authz_host" diff --git a/apache2/recipes/mod_authz_user.rb b/apache2/recipes/mod_authz_user.rb new file mode 100644 index 0000000..8dd46df --- /dev/null +++ b/apache2/recipes/mod_authz_user.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: authz_user +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "authz_user" diff --git a/apache2/recipes/mod_autoindex.rb b/apache2/recipes/mod_autoindex.rb new file mode 100644 index 0000000..622a66e --- /dev/null +++ b/apache2/recipes/mod_autoindex.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: autoindex +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "autoindex" do + conf true +end diff --git a/apache2/recipes/mod_cgi.rb b/apache2/recipes/mod_cgi.rb new file mode 100644 index 0000000..6c15a05 --- /dev/null +++ b/apache2/recipes/mod_cgi.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: cgi +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "cgi" diff --git a/apache2/recipes/mod_dav.rb b/apache2/recipes/mod_dav.rb new file mode 100644 index 0000000..fef656a --- /dev/null +++ b/apache2/recipes/mod_dav.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: dav +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "dav" diff --git a/apache2/recipes/mod_dav_svn.rb b/apache2/recipes/mod_dav_svn.rb new file mode 100644 index 0000000..ce50d54 --- /dev/null +++ b/apache2/recipes/mod_dav_svn.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: dav_svn +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "libapache2-svn" + +apache_module "dav_svn" diff --git a/apache2/recipes/mod_deflate.rb b/apache2/recipes/mod_deflate.rb new file mode 100644 index 0000000..b568f30 --- /dev/null +++ b/apache2/recipes/mod_deflate.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: deflate +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "deflate" do + conf true +end diff --git a/apache2/recipes/mod_dir.rb b/apache2/recipes/mod_dir.rb new file mode 100644 index 0000000..9930c3a --- /dev/null +++ b/apache2/recipes/mod_dir.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: dir +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "dir" do + conf true +end diff --git a/apache2/recipes/mod_env.rb b/apache2/recipes/mod_env.rb new file mode 100644 index 0000000..d345503 --- /dev/null +++ b/apache2/recipes/mod_env.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: env +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "env" diff --git a/apache2/recipes/mod_expires.rb b/apache2/recipes/mod_expires.rb new file mode 100644 index 0000000..9e5042e --- /dev/null +++ b/apache2/recipes/mod_expires.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: expires +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "expires" diff --git a/apache2/recipes/mod_fcgid.rb b/apache2/recipes/mod_fcgid.rb new file mode 100644 index 0000000..ea7ec3e --- /dev/null +++ b/apache2/recipes/mod_fcgid.rb @@ -0,0 +1,46 @@ +# +# Cookbook Name:: apache2 +# Recipe:: fcgid +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if platform?("debian", "ubuntu") + package "libapache2-mod-fcgid" +elsif platform?("centos", "redhat", "fedora") + package "mod_fcgid" do + notifies :run, resources(:execute => "generate-module-list"), :immediately + end + + file "#{node[:apache][:dir]}/conf.d/fcgid.conf" do + action :delete + backup false + end +elsif platform?("suse") + apache_lib_path = node[:architecture] == "i386" ? "/usr/lib/httpd" : "/usr/lib64/httpd" + package "httpd-devel" + bash "install-fcgid" do + code <<-EOH +(cd /tmp; wget http://superb-east.dl.sourceforge.net/sourceforge/mod-fcgid/mod_fcgid.2.2.tgz) +(cd /tmp; tar zxvf mod_fcgid.2.2.tgz) +(cd /tmp; perl -pi -e 's!/usr/local/apache2!#{apache_lib_path}!g' ./mod_fcgid.2.2/Makefile) +(cd /tmp/mod_fcgid.2.2; make install) +EOH + end +end + +apache_module "fcgid" do + conf true +end diff --git a/apache2/recipes/mod_headers.rb b/apache2/recipes/mod_headers.rb new file mode 100644 index 0000000..5e6b94d --- /dev/null +++ b/apache2/recipes/mod_headers.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: headers +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "headers" diff --git a/apache2/recipes/mod_ldap.rb b/apache2/recipes/mod_ldap.rb new file mode 100644 index 0000000..0877694 --- /dev/null +++ b/apache2/recipes/mod_ldap.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: ldap +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "ldap" diff --git a/apache2/recipes/mod_log_config.rb b/apache2/recipes/mod_log_config.rb new file mode 100644 index 0000000..fa8a484 --- /dev/null +++ b/apache2/recipes/mod_log_config.rb @@ -0,0 +1,24 @@ +# +# Cookbook Name:: apache2 +# Recipe:: log_config +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if platform?("centos", "redhat", "fedora", "suse") + apache_module "log_config" +else + include_recipe "apache2" +end diff --git a/apache2/recipes/mod_mime.rb b/apache2/recipes/mod_mime.rb new file mode 100644 index 0000000..16aee1a --- /dev/null +++ b/apache2/recipes/mod_mime.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: mime +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "mime" do + conf true +end diff --git a/apache2/recipes/mod_negotiation.rb b/apache2/recipes/mod_negotiation.rb new file mode 100644 index 0000000..348e11f --- /dev/null +++ b/apache2/recipes/mod_negotiation.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: negotiation +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "negotiation" do + conf true +end diff --git a/apache2/recipes/mod_php5.rb b/apache2/recipes/mod_php5.rb new file mode 100644 index 0000000..b6ae2c0 --- /dev/null +++ b/apache2/recipes/mod_php5.rb @@ -0,0 +1,32 @@ +# +# Cookbook Name:: apache2 +# Recipe:: php5 +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node[:platform] +when "debian", "ubuntu" + package "libapache2-mod-php5" do + action :install + end +when "centos", "redhat", "fedora" + package "php" do + action :install + notifies :run, resources(:execute => "generate-module-list"), :immediately + end +end + +apache_module "php5" diff --git a/apache2/recipes/mod_proxy.rb b/apache2/recipes/mod_proxy.rb new file mode 100644 index 0000000..fff7627 --- /dev/null +++ b/apache2/recipes/mod_proxy.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: proxy +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "proxy" do + conf true +end diff --git a/apache2/recipes/mod_proxy_ajp.rb b/apache2/recipes/mod_proxy_ajp.rb new file mode 100644 index 0000000..617a2c2 --- /dev/null +++ b/apache2/recipes/mod_proxy_ajp.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: proxy +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "proxy_ajp" diff --git a/apache2/recipes/mod_proxy_balancer.rb b/apache2/recipes/mod_proxy_balancer.rb new file mode 100644 index 0000000..dc62a71 --- /dev/null +++ b/apache2/recipes/mod_proxy_balancer.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: proxy +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "proxy_balancer" diff --git a/apache2/recipes/mod_proxy_connect.rb b/apache2/recipes/mod_proxy_connect.rb new file mode 100644 index 0000000..f41954f --- /dev/null +++ b/apache2/recipes/mod_proxy_connect.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: proxy +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "proxy_connect" diff --git a/apache2/recipes/mod_proxy_http.rb b/apache2/recipes/mod_proxy_http.rb new file mode 100644 index 0000000..ddff3ea --- /dev/null +++ b/apache2/recipes/mod_proxy_http.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: proxy_http +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "proxy_http" diff --git a/apache2/recipes/mod_python.rb b/apache2/recipes/mod_python.rb new file mode 100644 index 0000000..feaca03 --- /dev/null +++ b/apache2/recipes/mod_python.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: python +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "libapache2-mod-python" + +apache_module "python" diff --git a/apache2/recipes/mod_rewrite.rb b/apache2/recipes/mod_rewrite.rb new file mode 100644 index 0000000..df388a6 --- /dev/null +++ b/apache2/recipes/mod_rewrite.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: rewrite +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "rewrite" diff --git a/apache2/recipes/mod_setenvif.rb b/apache2/recipes/mod_setenvif.rb new file mode 100644 index 0000000..4048a5f --- /dev/null +++ b/apache2/recipes/mod_setenvif.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: setenvif +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "setenvif" do + conf true +end diff --git a/apache2/recipes/mod_ssl.rb b/apache2/recipes/mod_ssl.rb new file mode 100644 index 0000000..e82b9d1 --- /dev/null +++ b/apache2/recipes/mod_ssl.rb @@ -0,0 +1,42 @@ +# +# Cookbook Name:: apache2 +# Recipe:: ssl +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if platform?("centos", "redhat", "fedora") + package "mod_ssl" do + action :install + notifies :run, resources(:execute => "generate-module-list"), :immediately + end + + file "#{node[:apache][:dir]}/conf.d/ssl.conf" do + action :delete + backup false + end +end + +ports = node[:apache][:listen_ports].include?("443") ? node[:apache][:listen_ports] : [node[:apache][:listen_ports], "443"].flatten + +template "#{node[:apache][:dir]}/ports.conf" do + source "ports.conf.erb" + variables :apache_listen_ports => ports + notifies :restart, resources(:service => "apache2") +end + +apache_module "ssl" do + conf true +end diff --git a/apache2/recipes/mod_status.rb b/apache2/recipes/mod_status.rb new file mode 100644 index 0000000..3e71727 --- /dev/null +++ b/apache2/recipes/mod_status.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: status +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "status" do + conf true +end diff --git a/apache2/templates/default/a2dismod.erb b/apache2/templates/default/a2dismod.erb new file mode 100644 index 0000000..bcb73bf --- /dev/null +++ b/apache2/templates/default/a2dismod.erb @@ -0,0 +1,22 @@ +#!/bin/sh -e + +SYSCONFDIR='<%= @node[:apache][:dir] %>' + +if [ -z $1 ]; then + echo "Which module would you like to disable?" + echo -n "Your choices are: " + ls $SYSCONFDIR/mods-enabled/*.load | \ + sed -e "s,$SYSCONFDIR/mods-enabled/,,g" | sed -e 's/\.load$//g;' | xargs echo + echo -n "Module name? " + read MODNAME +else + MODNAME=$1 +fi + +if ! [ -e $SYSCONFDIR/mods-enabled/$MODNAME.load ]; then + echo "This module is already disabled, or does not exist!" + exit 1 +fi + +rm -f $SYSCONFDIR/mods-enabled/$MODNAME.* +echo "Module $MODNAME disabled; reload apache to fully disable." \ No newline at end of file diff --git a/apache2/templates/default/a2dissite.erb b/apache2/templates/default/a2dissite.erb new file mode 100644 index 0000000..7bcd80e --- /dev/null +++ b/apache2/templates/default/a2dissite.erb @@ -0,0 +1,29 @@ +#!/bin/sh -e + +SYSCONFDIR='<%= @node[:apache][:dir] %>' + +if [ -z $1 ]; then + echo "Which site would you like to disable?" + echo -n "Your choices are: " + ls $@node[:apache][:dir]/sites-enabled/* | \ + sed -e "s,$SYSCONFDIR/sites-enabled/,,g" | xargs echo + echo -n "Site name? " + read SITENAME +else + SITENAME=$1 +fi + +if [ $SITENAME = "default" ]; then + PRIORITY="000" +fi + +if ! [ -e $SYSCONFDIR/sites-enabled/$SITENAME -o \ + -e $SYSCONFDIR/sites-enabled/"$PRIORITY"-"$SITENAME" ]; then + echo "This site is already disabled, or does not exist!" + exit 1 +fi + +if ! rm $SYSCONFDIR/sites-enabled/$SITENAME 2>/dev/null; then + rm -f $SYSCONFDIR/sites-enabled/"$PRIORITY"-"$SITENAME" +fi +echo "Site $SITENAME disabled; reload apache to disable." \ No newline at end of file diff --git a/apache2/templates/default/a2enmod.erb b/apache2/templates/default/a2enmod.erb new file mode 100644 index 0000000..4579cbb --- /dev/null +++ b/apache2/templates/default/a2enmod.erb @@ -0,0 +1,37 @@ +#!/bin/sh -e + +SYSCONFDIR='<%= @node[:apache][:dir] %>' + +if [ -z $1 ]; then + echo "Which module would you like to enable?" + echo -n "Your choices are: " + ls $SYSCONFDIR/mods-available/*.load | \ + sed -e "s,$SYSCONFDIR/mods-available/,,g" | sed -e 's/\.load$//g;' | xargs echo + echo -n "Module name? " + read MODNAME +else + MODNAME=$1 +fi + +#figure out if we're on a prefork or threaded mpm +if [ -x /usr/sbin/apache2 ]; then + PREFORK=`/usr/sbin/apache2 -l | grep prefork || true` +fi + +if [ -e $SYSCONFDIR/mods-enabled/$MODNAME.load && -e $SYSCONFDIR/mods-enabled/$MODNAME.conf ]; then + echo "This module is already enabled!" + exit 0 +fi + +if ! [ -e $SYSCONFDIR/mods-available/$MODNAME.load ]; then + echo "This module does not exist!" + exit 1 +fi + +for i in conf load; do + if [ -e $SYSCONFDIR/mods-available/$MODNAME.$i -a ! -e $SYSCONFDIR/mods-enabled/$MODNAME.$i ]; then + ln -sf $SYSCONFDIR/mods-available/$MODNAME.$i $SYSCONFDIR/mods-enabled/$MODNAME.$i; + fi +done + +echo "Module $MODNAME installed; reload apache to enable." \ No newline at end of file diff --git a/apache2/templates/default/a2ensite.erb b/apache2/templates/default/a2ensite.erb new file mode 100644 index 0000000..68b3a15 --- /dev/null +++ b/apache2/templates/default/a2ensite.erb @@ -0,0 +1,38 @@ +#!/bin/sh -e + +SYSCONFDIR='<%= @node[:apache][:dir] %>' + +if [ -z $1 ]; then + echo "Which site would you like to enable?" + echo -n "Your choices are: " + ls $SYSCONFDIR/sites-available/* | \ + sed -e "s,$SYSCONFDIR/sites-available/,,g" | xargs echo + echo -n "Site name? " + read SITENAME +else + SITENAME=$1 +fi + +if [ $SITENAME = "default" ]; then + PRIORITY="000" +fi + +if [ -e $SYSCONFDIR/sites-enabled/$SITENAME -o \ + -e $SYSCONFDIR/sites-enabled/"$PRIORITY"-"$SITENAME" ]; then + echo "This site is already enabled!" + exit 0 +fi + +if ! [ -e $SYSCONFDIR/sites-available/$SITENAME ]; then + echo "This site does not exist!" + exit 1 +fi + +if [ $SITENAME = "default" ]; then + ln -sf $SYSCONFDIR/sites-available/$SITENAME \ + $SYSCONFDIR/sites-enabled/"$PRIORITY"-"$SITENAME" +else + ln -sf $SYSCONFDIR/sites-available/$SITENAME $SYSCONFDIR/sites-enabled/$SITENAME +fi + +echo "Site $SITENAME installed; reload apache to enable." \ No newline at end of file diff --git a/apache2/templates/default/apache2.conf.erb b/apache2/templates/default/apache2.conf.erb new file mode 100644 index 0000000..bbc72c7 --- /dev/null +++ b/apache2/templates/default/apache2.conf.erb @@ -0,0 +1,230 @@ +# +# Generated by Chef +# +# Based on the Ubuntu apache2.conf + +ServerRoot "<%= @node[:apache][:dir] %>" + +# +# The accept serialization lock file MUST BE STORED ON A LOCAL DISK. +# +<% if @node[:platform] == "debian" || @node[:platform] == "ubuntu" -%> +LockFile /var/lock/apache2/accept.lock +<% else %> +LockFile logs/accept.lock +<% end -%> + +# +# PidFile: The file in which the server should record its process +# identification number when it starts. +# +<% if @node[:platform] == "debian" || @node[:platform] == "ubuntu" -%> +PidFile /var/run/apache2.pid +<% elsif @node[:platform] == "centos" -%> +PidFile /var/run/httpd.pid +<% else -%> +PidFile logs/httpd.pid +<% end -%> + +# +# Timeout: The number of seconds before receives and sends time out. +# +Timeout <%= @node[:apache][:timeout] %> + +# +# KeepAlive: Whether or not to allow persistent connections (more than +# one request per connection). Set to "Off" to deactivate. +# +KeepAlive <%= @node[:apache][:keepalive] %> + +# +# MaxKeepAliveRequests: The maximum number of requests to allow +# during a persistent connection. Set to 0 to allow an unlimited amount. +# We recommend you leave this number high, for maximum performance. +# +MaxKeepAliveRequests <%= @node[:apache][:keepaliverequests] %> + +# +# KeepAliveTimeout: Number of seconds to wait for the next request from the +# same client on the same connection. +# +KeepAliveTimeout <%= @node[:apache][:keepalivetimeout] %> + +## +## Server-Pool Size Regulation (MPM specific) +## + +# prefork MPM +# StartServers: number of server processes to start +# MinSpareServers: minimum number of server processes which are kept spare +# MaxSpareServers: maximum number of server processes which are kept spare +# MaxClients: maximum number of server processes allowed to start +# MaxRequestsPerChild: maximum number of requests a server process serves + + StartServers <%= @node[:apache][:prefork][:startservers] %> + MinSpareServers <%= @node[:apache][:prefork][:minspareservers] %> + MaxSpareServers <%= @node[:apache][:prefork][:maxspareservers] %> + ServerLimit <%= @node[:apache][:prefork][:serverlimit] %> + MaxClients <%= @node[:apache][:prefork][:maxclients] %> + MaxRequestsPerChild <%= @node[:apache][:prefork][:maxrequestsperchild] %> + + +# worker MPM +# StartServers: initial number of server processes to start +# MaxClients: maximum number of simultaneous client connections +# MinSpareThreads: minimum number of worker threads which are kept spare +# MaxSpareThreads: maximum number of worker threads which are kept spare +# ThreadsPerChild: constant number of worker threads in each server process +# MaxRequestsPerChild: maximum number of requests a server process serves + + StartServers <%= @node[:apache][:worker][:startservers] %> + MaxClients <%= @node[:apache][:worker][:maxclients] %> + MinSpareThreads <%= @node[:apache][:worker][:minsparethreads] %> + MaxSpareThreads <%= @node[:apache][:worker][:maxsparethreads] %> + ThreadsPerChild <%= @node[:apache][:worker][:threadsperchild] %> + MaxRequestsPerChild <%= @node[:apache][:worker][:maxrequestsperchild] %> + + +User <%= @node[:apache][:user] %> +Group <%= @node[:apache][:user] %> + +# +# AccessFileName: The name of the file to look for in each directory +# for additional configuration directives. See also the AllowOverride +# directive. +# + +AccessFileName .htaccess + +# +# The following lines prevent .htaccess and .htpasswd files from being +# viewed by Web clients. +# + + Order allow,deny + Deny from all + + +# +# DefaultType is the default MIME type the server will use for a document +# if it cannot otherwise determine one, such as from filename extensions. +# If your server contains mostly text or HTML documents, "text/plain" is +# a good value. If most of your content is binary, such as applications +# or images, you may want to use "application/octet-stream" instead to +# keep browsers from trying to display binary files as though they are +# text. +# +DefaultType text/plain + + +# +# HostnameLookups: Log the names of clients or just their IP addresses +# e.g., www.apache.org (on) or 204.62.129.132 (off). +# The default is off because it'd be overall better for the net if people +# had to knowingly turn this feature on, since enabling it means that +# each client request will result in AT LEAST one lookup request to the +# nameserver. +# +HostnameLookups Off + +# ErrorLog: The location of the error log file. +# If you do not specify an ErrorLog directive within a +# container, error messages relating to that virtual host will be +# logged here. If you *do* define an error logfile for a +# container, that host's errors will be logged there and not here. +# +ErrorLog <%= @node[:apache][:log_dir] %>/error.log + +# +# LogLevel: Control the number of messages logged to the error_log. +# Possible values include: debug, info, notice, warn, error, crit, +# alert, emerg. +# +LogLevel warn + +# Include module configuration: +Include <%= @node[:apache][:dir] %>/mods-enabled/*.load +Include <%= @node[:apache][:dir] %>/mods-enabled/*.conf + +# Include ports listing +Include <%= @node[:apache][:dir] %>/ports.conf + +# +# The following directives define some format nicknames for use with +# a CustomLog directive (see below). +# +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined +LogFormat "%h %l %u %t \"%r\" %>s %b" common +LogFormat "%{Referer}i -> %U" referer +LogFormat "%{User-agent}i" agent +# + +# Customizable error responses come in three flavors: +# 1) plain text 2) local redirects 3) external redirects +# +# Some examples: +#ErrorDocument 500 "The server made a boo boo." +#ErrorDocument 404 /missing.html +#ErrorDocument 404 "/cgi-bin/missing_handler.pl" +#ErrorDocument 402 http://www.example.com/subscription_info.html +# + +# +# Putting this all together, we can internationalize error responses. +# +# We use Alias to redirect any /error/HTTP_.html.var response to +# our collection of by-error message multi-language collections. We use +# includes to substitute the appropriate text. +# +# You can modify the messages' appearance without changing any of the +# default HTTP_.html.var files by adding the line: +# +# Alias /error/include/ "/your/include/path/" +# +# which allows you to create your own set of files by starting with the +# /usr/share/apache2/error/include/ files and copying them to /your/include/path/, +# even on a per-VirtualHost basis. The default include files will display +# your Apache version number and your ServerAdmin email address regardless +# of the setting of ServerSignature. +# +# The internationalized error documents require mod_alias, mod_include +# and mod_negotiation. To activate them, uncomment the following 30 lines. + +# Alias /error/ "/usr/share/apache2/error/" +# +# +# AllowOverride None +# Options IncludesNoExec +# AddOutputFilter Includes html +# AddHandler type-map var +# Order allow,deny +# Allow from all +# LanguagePriority en cs de es fr it nl sv pt-br ro +# ForceLanguagePriority Prefer Fallback +# +# +# ErrorDocument 400 /error/HTTP_BAD_REQUEST.html.var +# ErrorDocument 401 /error/HTTP_UNAUTHORIZED.html.var +# ErrorDocument 403 /error/HTTP_FORBIDDEN.html.var +# ErrorDocument 404 /error/HTTP_NOT_FOUND.html.var +# ErrorDocument 405 /error/HTTP_METHOD_NOT_ALLOWED.html.var +# ErrorDocument 408 /error/HTTP_REQUEST_TIME_OUT.html.var +# ErrorDocument 410 /error/HTTP_GONE.html.var +# ErrorDocument 411 /error/HTTP_LENGTH_REQUIRED.html.var +# ErrorDocument 412 /error/HTTP_PRECONDITION_FAILED.html.var +# ErrorDocument 413 /error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var +# ErrorDocument 414 /error/HTTP_REQUEST_URI_TOO_LARGE.html.var +# ErrorDocument 415 /error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var +# ErrorDocument 500 /error/HTTP_INTERNAL_SERVER_ERROR.html.var +# ErrorDocument 501 /error/HTTP_NOT_IMPLEMENTED.html.var +# ErrorDocument 502 /error/HTTP_BAD_GATEWAY.html.var +# ErrorDocument 503 /error/HTTP_SERVICE_UNAVAILABLE.html.var +# ErrorDocument 506 /error/HTTP_VARIANT_ALSO_VARIES.html.var + + + +# Include generic snippets of statements +Include <%= @node[:apache][:dir] %>/conf.d/ + +# Include the virtual host configurations: +Include <%= @node[:apache][:dir] %>/sites-enabled/ diff --git a/apache2/templates/default/apache2.god.erb b/apache2/templates/default/apache2.god.erb new file mode 100644 index 0000000..b7315e4 --- /dev/null +++ b/apache2/templates/default/apache2.god.erb @@ -0,0 +1,19 @@ +God.watch do |w| + w.name = "apache2" + w.interval = 30.seconds # default + w.start = "<%= @params[:start] %>" + w.stop = "/etc/init.d/httpd stop" + w.restart = "<%= @params[:restart] %>" + w.start_grace = 10.seconds + w.restart_grace = 10.seconds + w.pid_file = "/var/run/httpd.pid" + w.behavior(:clean_pid_file) + + w.start_if do |start| + start.condition(:process_running) do |c| + c.interval = 5.seconds + c.running = false + c.notify = 'admin' + end + end +end diff --git a/apache2/templates/default/charset.erb b/apache2/templates/default/charset.erb new file mode 100644 index 0000000..40d7198 --- /dev/null +++ b/apache2/templates/default/charset.erb @@ -0,0 +1,6 @@ +# Read the documentation before enabling AddDefaultCharset. +# In general, it is only a good idea if you know that all your files +# have this encoding. It will override any encoding given in the files +# in meta http-equiv or xml encoding tags. + +#AddDefaultCharset UTF-8 diff --git a/apache2/templates/default/default-site.erb b/apache2/templates/default/default-site.erb new file mode 100644 index 0000000..d461bed --- /dev/null +++ b/apache2/templates/default/default-site.erb @@ -0,0 +1,57 @@ + + ServerAdmin <%= @node[:apache][:contact] %> + + DocumentRoot /var/www/ + + Options FollowSymLinks + AllowOverride None + + + Options Indexes FollowSymLinks MultiViews + AllowOverride None + Order allow,deny + allow from all + # This directive allows us to have apache2's default start page + # in /apache2-default/, but still have / go to the right place + #RedirectMatch ^/$ /apache2-default/ + + + ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ + + AllowOverride None + Options ExecCGI -MultiViews +SymLinksIfOwnerMatch + Order allow,deny + Allow from all + + + ErrorLog <%= @node[:apache][:log_dir] %>/error.log + + # Possible values include: debug, info, notice, warn, error, crit, + # alert, emerg. + LogLevel warn + + CustomLog <%= @node[:apache][:log_dir] %>/access.log combined + ServerSignature On + + Alias /doc/ "/usr/share/doc/" + + Options Indexes MultiViews FollowSymLinks + AllowOverride None + Order deny,allow + Deny from all + Allow from 127.0.0.0/255.0.0.0 ::1/128 + + + <% if @node[:platform] == "centos" || @node[:platform] == "redhat" || @node[:platform] == "fedora" -%> + # + # This configuration file enables the default "Welcome" + # page if there is no default index page present for + # the root URL. To disable the Welcome page, comment + # out all the lines below. + # + + Options -Indexes + ErrorDocument 403 /error/noindex.html + + <% end -%> + diff --git a/apache2/templates/default/mod_auth_openid.rb.erb b/apache2/templates/default/mod_auth_openid.rb.erb new file mode 100644 index 0000000..52dd8b0 --- /dev/null +++ b/apache2/templates/default/mod_auth_openid.rb.erb @@ -0,0 +1,12 @@ +#!/usr/bin/env ruby + +allowed_openids = Array.new +<% @node[:apache][:allowed_openids].each do |id| -%> +allowed_openids << "<%= id %>" +<% end -%> + +if allowed_openids.grep(ARGV[0]) + exit 0 +else + exit 1 +end diff --git a/apache2/templates/default/mods/README b/apache2/templates/default/mods/README new file mode 100644 index 0000000..df9f0bc --- /dev/null +++ b/apache2/templates/default/mods/README @@ -0,0 +1,2 @@ +These configs are taken from a Debian apache2.2-common 2.2.11-3 install. They +work on CentOS 5.3 with a few conditions using erb. diff --git a/apache2/templates/default/mods/alias.conf.erb b/apache2/templates/default/mods/alias.conf.erb new file mode 100644 index 0000000..d70670f --- /dev/null +++ b/apache2/templates/default/mods/alias.conf.erb @@ -0,0 +1,24 @@ + +# +# Aliases: Add here as many aliases as you need (with no limit). The format is +# Alias fakename realname +# +# Note that if you include a trailing / on fakename then the server will +# require it to be present in the URL. So "/icons" isn't aliased in this +# example, only "/icons/". If the fakename is slash-terminated, then the +# realname must also be slash terminated, and if the fakename omits the +# trailing slash, the realname must also omit it. +# +# We include the /icons/ alias for FancyIndexed directory listings. If +# you do not use FancyIndexing, you may comment this out. +# +Alias /icons/ "<%= @node[:apache][:icondir] %>" + +"> + Options Indexes MultiViews + AllowOverride None + Order allow,deny + Allow from all + + + diff --git a/apache2/templates/default/mods/authopenid.load.erb b/apache2/templates/default/mods/authopenid.load.erb new file mode 100644 index 0000000..f21882b --- /dev/null +++ b/apache2/templates/default/mods/authopenid.load.erb @@ -0,0 +1 @@ +LoadModule authopenid_module /usr/lib/apache2/modules/mod_auth_openid.so diff --git a/apache2/templates/default/mods/autoindex.conf.erb b/apache2/templates/default/mods/autoindex.conf.erb new file mode 100644 index 0000000..3839093 --- /dev/null +++ b/apache2/templates/default/mods/autoindex.conf.erb @@ -0,0 +1,101 @@ + +# +# Directives controlling the display of server-generated directory listings. +# + +# +# IndexOptions: Controls the appearance of server-generated directory +# listings. +# Remove/replace the "Charset=UTF-8" if you don't use UTF-8 for your filenames. +# +IndexOptions FancyIndexing VersionSort HTMLTable NameWidth=* DescriptionWidth=* Charset=UTF-8 + +# +# AddIcon* directives tell the server which icon to show for different +# files or filename extensions. These are only displayed for +# FancyIndexed directories. +# +AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip x-bzip2 + +AddIconByType (TXT,/icons/text.gif) text/* +AddIconByType (IMG,/icons/image2.gif) image/* +AddIconByType (SND,/icons/sound2.gif) audio/* +AddIconByType (VID,/icons/movie.gif) video/* + +AddIcon /icons/binary.gif .bin .exe +AddIcon /icons/binhex.gif .hqx +AddIcon /icons/tar.gif .tar +AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv +AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip +AddIcon /icons/a.gif .ps .ai .eps +AddIcon /icons/layout.gif .html .shtml .htm .pdf +AddIcon /icons/text.gif .txt +AddIcon /icons/c.gif .c +AddIcon /icons/p.gif .pl .py +AddIcon /icons/f.gif .for +AddIcon /icons/dvi.gif .dvi +AddIcon /icons/uuencoded.gif .uu +AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl +AddIcon /icons/tex.gif .tex +# It's a suffix rule, so simply matching "core" matches "score" as well ! +AddIcon /icons/bomb.gif /core +AddIcon (SND,/icons/sound2.gif) .ogg +AddIcon (VID,/icons/movie.gif) .ogm + +AddIcon /icons/back.gif .. +AddIcon /icons/hand.right.gif README +AddIcon /icons/folder.gif ^^DIRECTORY^^ +AddIcon /icons/blank.gif ^^BLANKICON^^ + +# Default icons for OpenDocument format +AddIcon /icons/odf6odt-20x22.png .odt +AddIcon /icons/odf6ods-20x22.png .ods +AddIcon /icons/odf6odp-20x22.png .odp +AddIcon /icons/odf6odg-20x22.png .odg +AddIcon /icons/odf6odc-20x22.png .odc +AddIcon /icons/odf6odf-20x22.png .odf +AddIcon /icons/odf6odb-20x22.png .odb +AddIcon /icons/odf6odi-20x22.png .odi +AddIcon /icons/odf6odm-20x22.png .odm + +AddIcon /icons/odf6ott-20x22.png .ott +AddIcon /icons/odf6ots-20x22.png .ots +AddIcon /icons/odf6otp-20x22.png .otp +AddIcon /icons/odf6otg-20x22.png .otg +AddIcon /icons/odf6otc-20x22.png .otc +AddIcon /icons/odf6otf-20x22.png .otf +AddIcon /icons/odf6oti-20x22.png .oti +AddIcon /icons/odf6oth-20x22.png .oth + +# +# DefaultIcon is which icon to show for files which do not have an icon +# explicitly set. +# +DefaultIcon /icons/unknown.gif + +# +# AddDescription allows you to place a short description after a file in +# server-generated indexes. These are only displayed for FancyIndexed +# directories. +# Format: AddDescription "description" filename +# +#AddDescription "GZIP compressed document" .gz +#AddDescription "tar archive" .tar +#AddDescription "GZIP compressed tar archive" .tgz + +# +# ReadmeName is the name of the README file the server will look for by +# default, and append to directory listings. +# +# HeaderName is the name of a file which should be prepended to +# directory indexes. +ReadmeName README.html +HeaderName HEADER.html + +# +# IndexIgnore is a set of filenames which directory indexing should ignore +# and not include in the listing. Shell-style wildcarding is permitted. +# +IndexIgnore .??* *~ *# RCS CVS *,v *,t + + diff --git a/apache2/templates/default/mods/deflate.conf.erb b/apache2/templates/default/mods/deflate.conf.erb new file mode 100644 index 0000000..2e41975 --- /dev/null +++ b/apache2/templates/default/mods/deflate.conf.erb @@ -0,0 +1,16 @@ + + AddOutputFilterByType DEFLATE text/html + AddOutputFilterByType DEFLATE text/css + AddOutputFilterByType DEFLATE text/plain + AddOutputFilterByType DEFLATE text/xml + AddOutputFilterByType DEFLATE application/xhtml+xml + AddOutputFilterByType DEFLATE application/xml + AddOutputFilterByType DEFLATE image/svg+xml + AddOutputFilterByType DEFLATE application/rss+xml + AddOutputFilterByType DEFLATE application/atom_xml + AddOutputFilterByType DEFLATE application/javascript + AddOutputFilterByType DEFLATE application/x-javascript + AddOutputFilterByType DEFLATE application/x-httpd-php + AddOutputFilterByType DEFLATE application/x-httpd-fastphp + AddOutputFilterByType DEFLATE application/x-httpd-eruby + diff --git a/apache2/templates/default/mods/dir.conf.erb b/apache2/templates/default/mods/dir.conf.erb new file mode 100644 index 0000000..e16fcb3 --- /dev/null +++ b/apache2/templates/default/mods/dir.conf.erb @@ -0,0 +1,5 @@ + + + DirectoryIndex index.html index.cgi index.pl index.php index.xhtml index.htm + + diff --git a/apache2/templates/default/mods/fcgid.conf.erb b/apache2/templates/default/mods/fcgid.conf.erb new file mode 100644 index 0000000..2141141 --- /dev/null +++ b/apache2/templates/default/mods/fcgid.conf.erb @@ -0,0 +1,10 @@ + + AddHandler fcgid-script .fcgi + IPCConnectTimeout 20 + + +<% if @node[:platform] == "centos" || @node[:platform] == "redhat" || @node[:platform] == "fedora" -%> +# Sane place to put sockets and shared memory file +SocketPath run/mod_fcgid +SharememPath run/mod_fcgid/fcgid_shm +<% end -%> diff --git a/apache2/templates/default/mods/mime.conf.erb b/apache2/templates/default/mods/mime.conf.erb new file mode 100644 index 0000000..b6954a3 --- /dev/null +++ b/apache2/templates/default/mods/mime.conf.erb @@ -0,0 +1,191 @@ + + +# +# TypesConfig points to the file containing the list of mappings from +# filename extension to MIME-type. +# +TypesConfig /etc/mime.types + +# +# AddType allows you to add to or override the MIME configuration +# file mime.types for specific file types. +# +#AddType application/x-gzip .tgz +# +# AddEncoding allows you to have certain browsers uncompress +# information on the fly. Note: Not all browsers support this. +# Despite the name similarity, the following Add* directives have +# nothing to do with the FancyIndexing customization directives above. +# +#AddEncoding x-compress .Z +#AddEncoding x-gzip .gz .tgz +#AddEncoding x-bzip2 .bz2 +# +# If the AddEncoding directives above are commented-out, then you +# probably should define those extensions to indicate media types: +# +AddType application/x-compress .Z +AddType application/x-gzip .gz .tgz +AddType application/x-bzip2 .bz2 + +# +# DefaultLanguage and AddLanguage allows you to specify the language of +# a document. You can then use content negotiation to give a browser a +# file in a language the user can understand. +# +# Specify a default language. This means that all data +# going out without a specific language tag (see below) will +# be marked with this one. You probably do NOT want to set +# this unless you are sure it is correct for all cases. +# +# * It is generally better to not mark a page as +# * being a certain language than marking it with the wrong +# * language! +# +# DefaultLanguage nl +# +# Note 1: The suffix does not have to be the same as the language +# keyword --- those with documents in Polish (whose net-standard +# language code is pl) may wish to use "AddLanguage pl .po" to +# avoid the ambiguity with the common suffix for perl scripts. +# +# Note 2: The example entries below illustrate that in some cases +# the two character 'Language' abbreviation is not identical to +# the two character 'Country' code for its country, +# E.g. 'Danmark/dk' versus 'Danish/da'. +# +# Note 3: In the case of 'ltz' we violate the RFC by using a three char +# specifier. There is 'work in progress' to fix this and get +# the reference data for rfc1766 cleaned up. +# +# Catalan (ca) - Croatian (hr) - Czech (cs) - Danish (da) - Dutch (nl) +# English (en) - Esperanto (eo) - Estonian (et) - French (fr) - German (de) +# Greek-Modern (el) - Hebrew (he) - Italian (it) - Japanese (ja) +# Korean (ko) - Luxembourgeois* (ltz) - Norwegian Nynorsk (nn) +# Norwegian (no) - Polish (pl) - Portugese (pt) +# Brazilian Portuguese (pt-BR) - Russian (ru) - Swedish (sv) +# Simplified Chinese (zh-CN) - Spanish (es) - Traditional Chinese (zh-TW) +# +AddLanguage ca .ca +AddLanguage cs .cz .cs +AddLanguage da .dk +AddLanguage de .de +AddLanguage el .el +AddLanguage en .en +AddLanguage eo .eo +# See README.Debian for Spanish +AddLanguage es .es +AddLanguage et .et +AddLanguage fr .fr +AddLanguage he .he +AddLanguage hr .hr +AddLanguage it .it +AddLanguage ja .ja +AddLanguage ko .ko +AddLanguage ltz .ltz +AddLanguage nl .nl +AddLanguage nn .nn +AddLanguage no .no +AddLanguage pl .po +AddLanguage pt .pt +AddLanguage pt-BR .pt-br +AddLanguage ru .ru +AddLanguage sv .sv +# See README.Debian for Turkish +AddLanguage tr .tr +AddLanguage zh-CN .zh-cn +AddLanguage zh-TW .zh-tw + +# +# Commonly used filename extensions to character sets. You probably +# want to avoid clashes with the language extensions, unless you +# are good at carefully testing your setup after each change. +# See http://www.iana.org/assignments/character-sets for the +# official list of charset names and their respective RFCs. +# +AddCharset us-ascii .ascii .us-ascii +AddCharset ISO-8859-1 .iso8859-1 .latin1 +AddCharset ISO-8859-2 .iso8859-2 .latin2 .cen +AddCharset ISO-8859-3 .iso8859-3 .latin3 +AddCharset ISO-8859-4 .iso8859-4 .latin4 +AddCharset ISO-8859-5 .iso8859-5 .cyr .iso-ru +AddCharset ISO-8859-6 .iso8859-6 .arb .arabic +AddCharset ISO-8859-7 .iso8859-7 .grk .greek +AddCharset ISO-8859-8 .iso8859-8 .heb .hebrew +AddCharset ISO-8859-9 .iso8859-9 .latin5 .trk +AddCharset ISO-8859-10 .iso8859-10 .latin6 +AddCharset ISO-8859-13 .iso8859-13 +AddCharset ISO-8859-14 .iso8859-14 .latin8 +AddCharset ISO-8859-15 .iso8859-15 .latin9 +AddCharset ISO-8859-16 .iso8859-16 .latin10 +AddCharset ISO-2022-JP .iso2022-jp .jis +AddCharset ISO-2022-KR .iso2022-kr .kis +AddCharset ISO-2022-CN .iso2022-cn .cis +AddCharset Big5 .Big5 .big5 .b5 +AddCharset cn-Big5 .cn-big5 +# For russian, more than one charset is used (depends on client, mostly): +AddCharset WINDOWS-1251 .cp-1251 .win-1251 +AddCharset CP866 .cp866 +AddCharset KOI8 .koi8 +AddCharset KOI8-E .koi8-e +AddCharset KOI8-r .koi8-r .koi8-ru +AddCharset KOI8-U .koi8-u +AddCharset KOI8-ru .koi8-uk .ua +AddCharset ISO-10646-UCS-2 .ucs2 +AddCharset ISO-10646-UCS-4 .ucs4 +AddCharset UTF-7 .utf7 +AddCharset UTF-8 .utf8 +AddCharset UTF-16 .utf16 +AddCharset UTF-16BE .utf16be +AddCharset UTF-16LE .utf16le +AddCharset UTF-32 .utf32 +AddCharset UTF-32BE .utf32be +AddCharset UTF-32LE .utf32le +AddCharset euc-cn .euc-cn +AddCharset euc-gb .euc-gb +AddCharset euc-jp .euc-jp +AddCharset euc-kr .euc-kr +#Not sure how euc-tw got in - IANA doesn't list it??? +AddCharset EUC-TW .euc-tw +AddCharset gb2312 .gb2312 .gb +AddCharset iso-10646-ucs-2 .ucs-2 .iso-10646-ucs-2 +AddCharset iso-10646-ucs-4 .ucs-4 .iso-10646-ucs-4 +AddCharset shift_jis .shift_jis .sjis + +# +# AddHandler allows you to map certain file extensions to "handlers": +# actions unrelated to filetype. These can be either built into the server +# or added with the Action directive (see below) +# +# To use CGI scripts outside of ScriptAliased directories: +# (You will also need to add "ExecCGI" to the "Options" directive.) +# +#AddHandler cgi-script .cgi + +# +# For files that include their own HTTP headers: +# +#AddHandler send-as-is asis + +# +# For server-parsed imagemap files: +# +#AddHandler imap-file map + +# +# For type maps (negotiated resources): +# (This is enabled by default to allow the Apache "It Worked" page +# to be distributed in multiple languages.) +# +AddHandler type-map var + +# +# Filters allow you to process content before it is sent to the client. +# +# To parse .shtml files for server-side includes (SSI): +# (You will also need to add "Includes" to the "Options" directive.) +# +AddType text/html .shtml +AddOutputFilter INCLUDES .shtml + + diff --git a/apache2/templates/default/mods/negotiation.conf.erb b/apache2/templates/default/mods/negotiation.conf.erb new file mode 100644 index 0000000..0e3455b --- /dev/null +++ b/apache2/templates/default/mods/negotiation.conf.erb @@ -0,0 +1,18 @@ + +# +# LanguagePriority allows you to give precedence to some languages +# in case of a tie during content negotiation. +# +# Just list the languages in decreasing order of preference. We have +# more or less alphabetized them here. You probably want to change this. +# +LanguagePriority en ca cs da de el eo es et fr he hr it ja ko ltz nl nn no pl pt pt-BR ru sv tr zh-CN zh-TW + +# +# ForceLanguagePriority allows you to serve a result page rather than +# MULTIPLE CHOICES (Prefer) [in case of a tie] or NOT ACCEPTABLE (Fallback) +# [in case no accepted languages matched the available variants] +# +ForceLanguagePriority Prefer Fallback + + diff --git a/apache2/templates/default/mods/proxy.conf.erb b/apache2/templates/default/mods/proxy.conf.erb new file mode 100644 index 0000000..46407a1 --- /dev/null +++ b/apache2/templates/default/mods/proxy.conf.erb @@ -0,0 +1,19 @@ + + #turning ProxyRequests on and allowing proxying from all may allow + #spammers to use your proxy to send email. + + ProxyRequests Off + + + AddDefaultCharset off + Order deny,allow + Deny from all + #Allow from .example.com + + + # Enable/disable the handling of HTTP/1.1 "Via:" headers. + # ("Full" adds the server version; "Block" removes all outgoing Via: headers) + # Set to one of: Off | On | Full | Block + + ProxyVia On + diff --git a/apache2/templates/default/mods/setenvif.conf.erb b/apache2/templates/default/mods/setenvif.conf.erb new file mode 100644 index 0000000..6b7d6e2 --- /dev/null +++ b/apache2/templates/default/mods/setenvif.conf.erb @@ -0,0 +1,28 @@ + + +# +# The following directives modify normal HTTP response behavior to +# handle known problems with browser implementations. +# +BrowserMatch "Mozilla/2" nokeepalive +BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0 +BrowserMatch "RealPlayer 4\.0" force-response-1.0 +BrowserMatch "Java/1\.0" force-response-1.0 +BrowserMatch "JDK/1\.0" force-response-1.0 + +# +# The following directive disables redirects on non-GET requests for +# a directory that does not include the trailing slash. This fixes a +# problem with Microsoft WebFolders which does not appropriately handle +# redirects for folders with DAV methods. +# Same deal with Apple's DAV filesystem and Gnome VFS support for DAV. +# +BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully +BrowserMatch "MS FrontPage" redirect-carefully +BrowserMatch "^WebDrive" redirect-carefully +BrowserMatch "^WebDAVFS/1.[012]" redirect-carefully +BrowserMatch "^gnome-vfs/1.0" redirect-carefully +BrowserMatch "^XML Spy" redirect-carefully +BrowserMatch "^Dreamweaver-WebDAV-SCM1" redirect-carefully + + diff --git a/apache2/templates/default/mods/ssl.conf.erb b/apache2/templates/default/mods/ssl.conf.erb new file mode 100644 index 0000000..14f72a6 --- /dev/null +++ b/apache2/templates/default/mods/ssl.conf.erb @@ -0,0 +1,72 @@ + +# +# Pseudo Random Number Generator (PRNG): +# Configure one or more sources to seed the PRNG of the SSL library. +# The seed data should be of good random quality. +# WARNING! On some platforms /dev/random blocks if not enough entropy +# is available. This means you then cannot use the /dev/random device +# because it would lead to very long connection times (as long as +# it requires to make more entropy available). But usually those +# platforms additionally provide a /dev/urandom device which doesn't +# block. So, if available, use this one instead. Read the mod_ssl User +# Manual for more details. +# +SSLRandomSeed startup builtin +SSLRandomSeed startup file:/dev/urandom 512 +SSLRandomSeed connect builtin +SSLRandomSeed connect file:/dev/urandom 512 + +## +## SSL Global Context +## +## All SSL configuration in this context applies both to +## the main server and all SSL-enabled virtual hosts. +## + +# +# Some MIME-types for downloading Certificates and CRLs +# +AddType application/x-x509-ca-cert .crt +AddType application/x-pkcs7-crl .crl + +# Pass Phrase Dialog: +# Configure the pass phrase gathering process. +# The filtering dialog program (`builtin' is a internal +# terminal dialog) has to provide the pass phrase on stdout. +SSLPassPhraseDialog builtin + +# Inter-Process Session Cache: +# Configure the SSL Session Cache: First the mechanism +# to use and second the expiring timeout (in seconds). +#SSLSessionCache dbm:/var/run/apache2/ssl_scache +<% if @node[:platform] == "centos" || @node[:platform] == "redhat" || @node[:platform] == "fedora" -%> +SSLSessionCache shmcb:/var/cache/mod_ssl/scache(512000) +<% else -%> +SSLSessionCache shmcb:/var/run/apache2/ssl_scache +<% end -%> +SSLSessionCacheTimeout 300 + +# Semaphore: +# Configure the path to the mutual exclusion semaphore the +# SSL engine uses internally for inter-process synchronization. +<% if @node[:platform] == "centos" || @node[:platform] == "redhat" || @node[:platform] == "fedora" -%> +SSLMutex default +<% else -%> +SSLMutex file:/var/run/apache2/ssl_mutex +<% end -%> + +# SSL Cipher Suite: +# List the ciphers that the client is permitted to negotiate. +# See the mod_ssl documentation for a complete list. +# enable only secure ciphers: +SSLCipherSuite HIGH:MEDIUM:!ADH +# Use this instead if you want to allow cipher upgrades via SGC facility. +# In this case you also have to use something like +# SSLRequire %{SSL_CIPHER_USEKEYSIZE} >= 128 +# see http://httpd.apache.org/docs/2.2/ssl/ssl_howto.html.en#upgradeenc +#SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL + +# enable only secure protocols: SSLv3 and TLSv1, but not SSLv2 +SSLProtocol all -SSLv2 + + diff --git a/apache2/templates/default/mods/status.conf.erb b/apache2/templates/default/mods/status.conf.erb new file mode 100644 index 0000000..679d111 --- /dev/null +++ b/apache2/templates/default/mods/status.conf.erb @@ -0,0 +1,16 @@ + +# +# Allow server status reports generated by mod_status, +# with the URL of http://servername/server-status +# Uncomment and change the ".example.com" to allow +# access from other hosts. +# + + SetHandler server-status + Order deny,allow + Deny from all + Allow from localhost ip6-localhost +# Allow from .example.com + + + diff --git a/apache2/templates/default/port_apache.erb b/apache2/templates/default/port_apache.erb new file mode 100644 index 0000000..f6078dd --- /dev/null +++ b/apache2/templates/default/port_apache.erb @@ -0,0 +1,2 @@ +# Port <%= @port %> +-A FWR -p tcp -m tcp --dport <%= @port %> -j ACCEPT \ No newline at end of file diff --git a/apache2/templates/default/ports.conf.erb b/apache2/templates/default/ports.conf.erb new file mode 100644 index 0000000..cc3631e --- /dev/null +++ b/apache2/templates/default/ports.conf.erb @@ -0,0 +1,6 @@ +#This file generated via template by Chef. +<% @apache_listen_ports.each do |port| -%> +Listen <%= port %> +NameVirtualHost *:<%= port %> + +<% end -%> diff --git a/apache2/templates/default/security.erb b/apache2/templates/default/security.erb new file mode 100644 index 0000000..dbb1a47 --- /dev/null +++ b/apache2/templates/default/security.erb @@ -0,0 +1,50 @@ +# +# Disable access to the entire file system except for the directories that +# are explicitly allowed later. +# +# This currently breaks the configurations that come with some web application +# Debian packages. It will be made the default for the release after lenny. +# +# +# AllowOverride None +# Order Deny,Allow +# Deny from all +# + + +# Changing the following options will not really affect the security of the +# server, but might make attacks slightly more difficult in some cases. + +# +# ServerTokens +# This directive configures what you return as the Server HTTP response +# Header. The default is 'Full' which sends information about the OS-Type +# and compiled in modules. +# Set to one of: Full | OS | Minimal | Minor | Major | Prod +# where Full conveys the most information, and Prod the least. +# +#ServerTokens Minimal +ServerTokens <%= @node[:apache][:servertokens] %> + +# +# Optionally add a line containing the server version and virtual host +# name to server-generated pages (internal error documents, FTP directory +# listings, mod_status and mod_info output etc., but not CGI generated +# documents or custom error documents). +# Set to "EMail" to also include a mailto: link to the ServerAdmin. +# Set to one of: On | Off | EMail +# +#ServerSignature Off +ServerSignature <%= @node[:apache][:serversignature] %> + +# +# Allow TRACE method +# +# Set to "extended" to also reflect the request body (only for testing and +# diagnostic purposes). +# +# Set to one of: On | Off | extended +# +#TraceEnable Off +TraceEnable <%= @node[:apache][:traceenable] %> + diff --git a/apache2/templates/default/web_app.conf.erb b/apache2/templates/default/web_app.conf.erb new file mode 100644 index 0000000..d6a12c4 --- /dev/null +++ b/apache2/templates/default/web_app.conf.erb @@ -0,0 +1,43 @@ + + ServerName <%= @params[:server_name] %> + ServerAlias <% @params[:server_aliases].each do |a| %><%= "#{a}" %> <% end %> + DocumentRoot <%= @params[:docroot] %> + RewriteEngine On + + > + Options FollowSymLinks + AllowOverride None + Order allow,deny + Allow from all + + + + Options FollowSymLinks + AllowOverride None + + + + SetHandler server-status + + Order Deny,Allow + Deny from all + Allow from 127.0.0.1 + + + LogLevel info + ErrorLog <%= @node[:apache][:log_dir] %>/<%= @params[:name] %>-error.log + CustomLog <%= @node[:apache][:log_dir] %>/<%= @params[:name] %>-access.log combined + + RewriteEngine On + RewriteLog <%= @node[:apache][:log_dir] %>/<%= @application_name %>-rewrite.log + RewriteLogLevel 0 + + # Canonical host, <%= @params[:server_name] %> + RewriteCond %{HTTP_HOST} !^<%= @params[:server_name] %> [NC] + RewriteCond %{HTTP_HOST} !^$ + RewriteRule ^/(.*)$ http://<%= @params[:server_name] %>/$1 [L,R=301] + + RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f + RewriteCond %{SCRIPT_FILENAME} !maintenance.html + RewriteRule ^.*$ /system/maintenance.html [L] + \ No newline at end of file diff --git a/apparmor/metadata.json b/apparmor/metadata.json new file mode 100644 index 0000000..01bfb09 --- /dev/null +++ b/apparmor/metadata.json @@ -0,0 +1,40 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Disables apparmor service on Ubuntu", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "apparmor": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ] + }, + "version": "0.7.0", + "name": "apparmor", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "apparmor": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/apparmor/metadata.rb b/apparmor/metadata.rb new file mode 100644 index 0000000..d225763 --- /dev/null +++ b/apparmor/metadata.rb @@ -0,0 +1,6 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Disables apparmor service on Ubuntu" +version "0.8" +supports "ubuntu" diff --git a/apparmor/recipes/default.rb b/apparmor/recipes/default.rb new file mode 100644 index 0000000..bbdb43b --- /dev/null +++ b/apparmor/recipes/default.rb @@ -0,0 +1,26 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: apparmor +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node[:platform] +when "ubuntu" + service "apparmor" do + action [ :stop, :disable ] + end +end diff --git a/apt/files/default/apt-cacher b/apt/files/default/apt-cacher new file mode 100644 index 0000000..dab9488 --- /dev/null +++ b/apt/files/default/apt-cacher @@ -0,0 +1,9 @@ +# apt-cacher startup configuration file + +# IMPORTANT: check the apt-cacher.conf file before using apt-cacher as daemon. + +# set to 1 to start the daemon at boot time +AUTOSTART=1 + +# extra settings to override the ones in apt-cacher.conf +# EXTRAOPT=" daemon_port=3142 limit=30 " diff --git a/apt/files/default/apt-cacher.conf b/apt/files/default/apt-cacher.conf new file mode 100644 index 0000000..32ca3c3 --- /dev/null +++ b/apt/files/default/apt-cacher.conf @@ -0,0 +1,144 @@ +# This file has been modified by ./apt-proxy-to-apt-cacher +# Some lines may have been appended at the bottom of this file +# This file has been modified by /usr/share/apt-cacher/apt-proxy-to-apt-cacher +# Some lines may have been appended at the bottom of this file +################################################################# +# This is the config file for apt-cacher. On most Debian systems +# you can safely leave the defaults alone. +################################################################# + +# cache_dir is used to set the location of the local cache. This can +# become quite large, so make sure it is somewhere with plenty of space. +cache_dir=/var/cache/apt-cacher + +# The email address of the administrator is displayed in the info page +# and traffic reports. +admin_email=root@localhost + +# For the daemon startup settings please edit the file /etc/default/apt-cacher. + +# Daemon port setting, only useful in stand-alone mode. You need to run the +# daemon as root to use privileged ports (<1024). +daemon_port = 3142 + +# optional settings, user and group to run the daemon as. Make sure they have +# sufficient permissions on the cache and log directories. Comment the settings +# to run apt-cacher as the native user. +group=www-data +user=www-data + +# optional setting, binds the listening daemon to one specified IP. Use IP +# ranges for more advanced configuration, see below. +# daemon_addr=localhost + +# If your apt-cacher machine is directly exposed to the Internet and you are +# worried about unauthorised machines fetching packages through it, you can +# specify a list of IPv4 addresses which are allowed to use it and another +# list of IPv4 addresses which aren't. +# Localhost (127.0.0.1) is always allowed. Other addresses must be matched +# by allowed_hosts and not by denied_hosts to be permitted to use the cache. +# Setting allowed_hosts to "*" means "allow all". +# Otherwise the format is a comma-separated list containing addresses, +# optionally with masks (like 10.0.0.0/22), or ranges of addresses (two +# addresses separated by a hyphen, no masks, like '192.168.0.3-192.168.0.56'). +allowed_hosts=* +denied_hosts= + +# And similiarly for IPv6 with allowed_hosts_6 and denied_hosts_6. +# Note that IPv4-mapped IPv6 addresses (::ffff:w.x.y.z) are truncated to +# w.x.y.z and are handled as IPv4. +allowed_hosts_6=fec0::/16 +denied_hosts_6= + +# This thing can be done by Apache but is much simplier here - limit access to +# Debian mirrors based on server names in the URLs +#allowed_locations=ftp.uni-kl.de,ftp.nerim.net,debian.tu-bs.de + +# Apt-cacher can generate usage reports every 24 hours if you set this +# directive to 1. You can view the reports in a web browser by pointing +# to your cache machine with '/apt-cacher/report' on the end, like this: +# http://yourcache.example.com/apt-cacher/report +# Generating reports is very fast even with many thousands of logfile +# lines, so you can safely turn this on without creating much +# additional system load. +generate_reports=1 + +# Apt-cacher can clean up its cache directory every 24 hours if you set +# this directive to 1. Cleaning the cache can take some time to run +# (generally in the order of a few minutes) and removes all package +# files that are not mentioned in any existing 'Packages' lists. This +# has the effect of deleting packages that have been superseded by an +# updated 'Packages' list. +clean_cache=1 + +# The directory to use for apt-cacher access and error logs. +# The access log records every request in the format: +# date-time|client ip address|HIT/MISS/EXPIRED|object size|object name +# The error log is slightly more free-form, and is also used for debug +# messages if debug mode is turned on. +# Note that the old 'logfile' and 'errorfile' directives are +# deprecated: if you set them explicitly they will be honoured, but it's +# better to just get rid of them from old config files. +logdir=/var/log/apt-cacher + +# apt-cacher can use different methods to decide whether package lists need to +# be updated, +# A) looking at the age of the cached files +# B) getting HTTP header from server and comparing that with cached data. This +# method is more reliable and avoids desynchronisation of data and index files +# but needs to transfer few bytes from the server every time somebody requests +# the files ("apt-get update") +# Set the following value to the maximum age (in hours) for method A or to 0 +# for method B +expire_hours=0 + +# Apt-cacher can pass all its requests to an external http proxy like +# Squid, which could be very useful if you are using an ISP that blocks +# port 80 and requires all web traffic to go through its proxy. The +# format is 'hostname:port', eg: 'proxy.example.com:8080'. +http_proxy=proxy.example.com:8080 + +# Use of an external proxy can be turned on or off with this flag. +# Value should be either 0 (off) or 1 (on). +use_proxy=0 + +# External http proxy sometimes need authentication to get full access. The +# format is 'username:password'. +http_proxy_auth=proxyuser:proxypass + +# Use of external proxy authentication can be turned on or off with this flag. +# Value should be either 0 (off) or 1 (on). +use_proxy_auth=0 + +# Rate limiting sets the maximum bandwidth in bytes per second to use +# for fetching packages. Syntax is fully defined in 'man wget'. +# Use 'k' or 'm' to use kilobits or megabits / second: eg, 'limit=25k'. +# Use 0 or a negative value for no rate limiting. +limit=0 + +# Debug mode makes apt-cacher spew a lot of extra debug junk to the +# error log (whose location is defined with the 'logdir' directive). +# Leave this off unless you need it, or your error log will get very +# big. Acceptable values are 0 or 1. +debug=0 + +# Adapt the line in the usage info web page to match your server configuration +# example_sources_line=deb http://my.cacher.server:3142/ftp.au.debian.org/debian unstable main contrib non-free + +# Print a 410 (Gone) HTTP message with the specified text when accessed via +# CGI. Useful to tell users to adapt their sources.list files when the +# apt-cacher server is beeing relocated (via apt-get's error messages while +# running "update") +#cgi_advise_to_use = Please use http://cacheserver:3142/ as apt-cacher access URL +#cgi_advise_to_use = Server relocated. To change sources.list, run perl -pe "s,/apt-cacher\??,:3142," -i /etc/apt/sources.list + +# Server mapping - this allows to hide real server names behind virtual paths +# that appear in the access URL. This method is known from apt-proxy. This is +# also the only method to use FTP access to the target hosts. The syntax is simple, the part of the beginning to replace, followed by a list of mirror urls, all space separated. Multiple profile are separated by semicolons +# path_map = debian ftp.uni-kl.de/pub/linux/debian ftp2.de.debian.org/debian ; ubuntu archive.ubuntu.com/ubuntu ; security security.debian.org/debian-security ftp2.de.debian.org/debian-security +# Note that you need to specify all target servers in the allowed_locations +# options if you make use of it. Also note that the paths should not overlap +# each other. FTP access method not supported yet, maybe in the future. + +# extra setting from apt-proxy configuration +path_map = ubuntu us.archive.ubuntu.com/ubuntu ; ubuntu-security security.ubuntu.com/ubuntu ; debian debian.osuosl.org/debian/ ; security security.debian.org/debian-security diff --git a/apt/files/default/apt-proxy-v2.conf b/apt/files/default/apt-proxy-v2.conf new file mode 100644 index 0000000..6541f25 --- /dev/null +++ b/apt/files/default/apt-proxy-v2.conf @@ -0,0 +1,50 @@ +[DEFAULT] +;; All times are in seconds, but you can add a suffix +;; for minutes(m), hours(h) or days(d) + +;; commented out address so apt-proxy will listen on all IPs +;; address = 127.0.0.1 +port = 9999 +cache_dir = /var/cache/apt-proxy + +;; Control files (Packages/Sources/Contents) refresh rate +min_refresh_delay = 1s +complete_clientless_downloads = 1 + +;; Debugging settings. +debug = all:4 db:0 + +time = 30 +passive_ftp = on + +;;-------------------------------------------------------------- +;; Cache housekeeping + +cleanup_freq = 1d +max_age = 120d +max_versions = 3 + +;;--------------------------------------------------------------- +;; Backend servers +;; +;; Place each server in its own [section] + +[ubuntu] +; Ubuntu archive +backends = + http://us.archive.ubuntu.com/ubuntu + +[ubuntu-security] +; Ubuntu security updates +backends = http://security.ubuntu.com/ubuntu + +[debian] +;; Backend servers, in order of preference +backends = + http://debian.osuosl.org/debian/ + +[security] +;; Debian security archive +backends = + http://security.debian.org/debian-security + http://ftp2.de.debian.org/debian-security diff --git a/apt/metadata.json b/apt/metadata.json new file mode 100644 index 0000000..c37fe61 --- /dev/null +++ b/apt/metadata.json @@ -0,0 +1,51 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Configures apt and apt services", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "apt::proxy": "Set up an APT proxy", + "apt": "", + "apt::cacher": "Set up an APT cache" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.8.0", + "name": "apt", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "apt::proxy": [ + + ], + "apt": [ + + ], + "apt::cacher": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/apt/metadata.rb b/apt/metadata.rb new file mode 100644 index 0000000..cb6e712 --- /dev/null +++ b/apt/metadata.rb @@ -0,0 +1,11 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Configures apt and apt services" +version "0.8" +recipe "apt::cacher", "Set up an APT cache" +recipe "apt::proxy", "Set up an APT proxy" + +%w{ ubuntu debian }.each do |os| + supports os +end diff --git a/apt/recipes/cacher.rb b/apt/recipes/cacher.rb new file mode 100644 index 0000000..2377408 --- /dev/null +++ b/apt/recipes/cacher.rb @@ -0,0 +1,42 @@ +# +# Cookbook Name:: apt +# Recipe:: cacher +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +package "apt-cacher" do + action :install +end + +service "apt-cacher" do + supports :restart => true, :status => false + action [ :enable, :start ] +end + +remote_file "/etc/apt-cacher/apt-cacher.conf" do + source "apt-cacher.conf" + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "apt-cacher") +end + +remote_file "/etc/default/apt-cacher" do + source "apt-cacher" + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "apt-cacher") +end diff --git a/apt/recipes/default.rb b/apt/recipes/default.rb new file mode 100644 index 0000000..d1117a7 --- /dev/null +++ b/apt/recipes/default.rb @@ -0,0 +1,33 @@ +# +# Cookbook Name:: apt +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +e = execute "apt-get update" do + action :nothing +end + +e.run_action(:run) + +%w{/var/cache/local /var/cache/local/preseeding}.each do |dirname| + directory dirname do + owner "root" + group "root" + mode 0755 + action :create + end +end diff --git a/apt/recipes/proxy.rb b/apt/recipes/proxy.rb new file mode 100644 index 0000000..3eede34 --- /dev/null +++ b/apt/recipes/proxy.rb @@ -0,0 +1,34 @@ +# +# Cookbook Name:: apt +# Recipe:: proxy +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +package "apt-proxy" do + action :install +end + +service "apt-proxy" do + supports :restart => true, :status => false + action [ :enable, :start ] +end + +remote_file "/etc/apt-proxy/apt-proxy-v2.conf" do + source "apt-proxy-v2.conf" + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "apt-proxy") +end diff --git a/aws/libraries/ec2.rb b/aws/libraries/ec2.rb new file mode 100644 index 0000000..b7c2a46 --- /dev/null +++ b/aws/libraries/ec2.rb @@ -0,0 +1,56 @@ +# TODO: once sync_libraries properly handles sub-directories, move this file to aws/libraries/opscode/aws/ec2.rb + +begin + require 'right_aws' +rescue LoadError + Chef::Log.warn("Missing gem 'right_aws'") +end + +require 'open-uri' + +module Opscode + module Aws + module Ec2 + def find_snapshot_id(volume_id="") + snapshot_id = nil + ec2.describe_snapshots.sort { |a,b| b[:aws_started_at] <=> a[:aws_started_at] }.each do |snapshot| + if snapshot[:aws_volume_id] == volume_id + snapshot_id = snapshot[:aws_id] + end + end + raise "Cannot find snapshot id!" unless snapshot_id + Chef::Log.debug("Snapshot ID is #{snapshot_id}") + snapshot_id + end + + def ec2 + @@ec2 ||= RightAws::Ec2.new(new_resource.aws_access_key, new_resource.aws_secret_access_key, { :logger => Chef::Log }) + end + + def instance_id + @@instance_id ||= query_instance_id + end + + def instance_availability_zone + @@instance_availability_zone ||= query_instance_availability_zone + end + + private + + def query_instance_id + instance_id = open('http://169.254.169.254/latest/meta-data/instance-id'){|f| f.gets} + raise "Cannot find instance id!" unless instance_id + Chef::Log.debug("Instance ID is #{instance_id}") + instance_id + end + + def query_instance_availability_zone + availability_zone = open('http://169.254.169.254/latest/meta-data/placement/availability-zone/'){|f| f.gets} + raise "Cannot find availability zone!" unless availability_zone + Chef::Log.debug("Instance's availability zone is #{availability_zone}") + availability_zone + end + + end + end +end diff --git a/aws/providers/ebs_volume.rb b/aws/providers/ebs_volume.rb new file mode 100644 index 0000000..75b7059 --- /dev/null +++ b/aws/providers/ebs_volume.rb @@ -0,0 +1,222 @@ +include Opscode::Aws::Ec2 + +action :create do + raise "Cannot create a volume with a specific id (EC2 chooses volume ids)" if new_resource.volume_id + if new_resource.snapshot_id =~ /vol/ + new_resource.snapshot_id(find_snapshot_id(new_resource.snapshot_id)) + end + + nvid = volume_id_in_node_data + if nvid + # volume id is registered in the node data, so check that the volume in fact exists in EC2 + vol = volume_by_id(nvid) + exists = vol && vol[:aws_status] != "deleting" + # TODO: determine whether this should be an error or just cause a new volume to be created. Currently erring on the side of failing loudly + raise "Volume with id #{nvid} is registered with the node but does not exist in EC2. To clear this error, remove the ['aws']['ebs_volume']['#{new_resource.name}']['volume_id'] entry from this node's data." unless exists + else + # Determine if there is a volume that meets the resource's specifications and is attached to the current + # instance in case a previous [:create, :attach] run created and attached a volume but for some reason was + # not registered in the node data (e.g. an exception is thrown after the attach_volume request was accepted + # by EC2, causing the node data to not be stored on the server) + if new_resource.device && (attached_volume = currently_attached_volume(instance_id, new_resource.device)) + Chef::Log.debug("There is already a volume attached at device #{new_resource.device}") + compatible = volume_compatible_with_resource_definition?(attached_volume) + raise "Volume #{attached_volume[:aws_id]} attached at #{attached_volume[:aws_device]} but does not conform to this resource's specifications" unless compatible + Chef::Log.debug("The volume matches the resource's definition, so the volume is assumed to be already created") + node.set[:aws][:ebs_volume][new_resource.name][:volume_id] = attached_volume[:aws_id] + else + # If not, create volume and register its id in the node data + nvid = create_volume(new_resource.snapshot_id, new_resource.size, new_resource.availability_zone, new_resource.timeout) + node.set[:aws][:ebs_volume][new_resource.name][:volume_id] = nvid + new_resource.updated = true + end + node.save + end +end + +action :attach do + vol = determine_volume + + if vol[:aws_status] == "in-use" + if vol[:aws_instance_id] != instance_id + raise "Volume with id #{vol[:aws_id]} exists but is attached to instance #{vol[:aws_instance_id]}" + else + Chef::Log.debug("Volume is already attached") + end + else + # attach the volume and register its id in the node data + attach_volume(vol[:aws_id], instance_id, new_resource.device, new_resource.timeout) + node.set[:aws][:ebs_volume][new_resource.name][:volume_id] = vol[:aws_id] + node.save + new_resource.updated = true + end +end + +action :detach do + vol = determine_volume + return if vol[:aws_instance_id] != instance_id + detach_volume(vol[:aws_id], new_resource.timeout) + new_resource.updated = true +end + +action :snapshot do + vol = determine_volume + snapshot = ec2.create_snapshot(vol[:aws_id]) + new_resource.updated = true + Chef::Log.info("Created snapshot of #{vol[:aws_id]} as #{snapshot[:aws_id]}") +end + +action :prune do + vol = determine_volume + old_snapshots = Array.new + Chef::Log.info "Checking for old snapshots" + ec2.describe_snapshots.sort { |a,b| b[:aws_started_at] <=> a[:aws_started_at] }.each do |snapshot| + if snapshot[:aws_volume_id] == vol[:aws_id] + Chef::Log.info "Found old snapshot #{snapshot[:aws_id]} (#{snapshot[:aws_volume_id]}) #{snapshot[:aws_started_at]}" + old_snapshots << snapshot + end + end + if old_snapshots.length >= new_resource.snapshots_to_keep + old_snapshots[new_resource.snapshots_to_keep - 1, old_snapshots.length].each do |die| + Chef::Log.info "Deleting old snapshot #{die[:aws_id]}" + ec2.delete_snapshot(die[:aws_id]) + new_resource.updated = true + end + end +end + +private + +def volume_id_in_node_data + begin + node[:aws][:ebs_volume][new_resource.name][:volume_id] + rescue NoMethodError => e + nil + end +end + +# Pulls the volume id from the volume_id attribute or the node data and verifies that the volume actually exists +def determine_volume + vol_id = new_resource.volume_id || volume_id_in_node_data || currently_attached_volume(instance_id, new_resource.device) + raise "volume_id attribute not set and no volume id is set in the node data for this resource (which is populated by action :create)" unless vol_id + + # check that volume exists + vol = volume_by_id(vol_id) + raise "No volume with id #{vol_id} exists" unless vol + + vol +end + +# Retrieves information for a volume +def volume_by_id(volume_id) + ec2.describe_volumes.find{|v| v[:aws_id] == volume_id} +end + +# Returns the volume that's attached to the instance at the given device or nil if none matches +def currently_attached_volume(instance_id, device) + ec2.describe_volumes.find{|v| v[:aws_instance_id] == instance_id && v[:aws_device] == device} +end + +# Returns true if the given volume meets the resource's attributes +def volume_compatible_with_resource_definition?(volume) + if new_resource.snapshot_id =~ /vol/ + new_resource.snapshot_id(find_snapshot_id(new_resource.snapshot_id)) + end + (new_resource.size.nil? || new_resource.size == volume[:aws_size]) && + (new_resource.availability_zone.nil? || new_resource.availability_zone == volume[:zone]) && + (new_resource.snapshot_id == volume[:snapshot_id]) +end + +# Creates a volume according to specifications and blocks until done (or times out) +def create_volume(snapshot_id, size, availability_zone, timeout) + availability_zone ||= instance_availability_zone + nv = ec2.create_volume(snapshot_id, size, availability_zone) + Chef::Log.debug("Created new volume #{nv[:aws_id]}#{snapshot_id ? " based on #{snapshot_id}" : ""}") + + # block until created + begin + Timeout::timeout(timeout) do + while true + vol = volume_by_id(nv[:aws_id]) + if vol && vol[:aws_status] != "deleting" + if ["in-use", "available"].include?(vol[:aws_status]) + Chef::Log.info("Volume #{nv[:aws_id]} is available") + break + else + Chef::Log.debug("Volume is #{vol[:aws_status]}") + end + sleep 3 + else + raise "Volume #{nv[:aws_id]} no longer exists" + end + end + end + rescue Timeout::Error + raise "Timed out waiting for volume creation after #{timeout} seconds" + end + + nv[:aws_id] +end + +# Attaches the volume and blocks until done (or times out) +def attach_volume(volume_id, instance_id, device, timeout) + Chef::Log.debug("Attaching #{volume_id} as #{device}") + ec2.attach_volume(volume_id, instance_id, device) + + # block until attached + begin + Timeout::timeout(timeout) do + while true + vol = volume_by_id(volume_id) + if vol && vol[:aws_status] != "deleting" + if vol[:aws_attachment_status] == "attached" + if vol[:aws_instance_id] == instance_id + Chef::Log.info("Volume #{volume_id} is attached to #{instance_id}") + break + else + raise "Volume is attached to instance #{vol[:aws_instance_id]} instead of #{instance_id}" + end + else + Chef::Log.debug("Volume is #{vol[:aws_status]}") + end + sleep 3 + else + raise "Volume #{volume_id} no longer exists" + end + end + end + rescue Timeout::Error + raise "Timed out waiting for volume attachment after #{timeout} seconds" + end +end + +# Detaches the volume and blocks until done (or times out) +def detach_volume(volume_id, timeout) + Chef::Log.debug("Detaching #{volume_id}") + vol = volume_by_id(volume_id) + orig_instance_id = vol[:aws_instance_id] + ec2.detach_volume(volume_id) + + # block until detached + begin + Timeout::timeout(timeout) do + while true + vol = volume_by_id(volume_id) + if vol && vol[:aws_status] != "deleting" + if vol[:aws_instance_id] != orig_instance_id + Chef::Log.info("Volume detached from #{orig_instance_id}") + break + else + Chef::Log.debug("Volume: #{vol.inspect}") + end + else + Chef::Log.debug("Volume #{volume_id} no longer exists") + break + end + sleep 3 + end + end + rescue Timeout::Error + raise "Timed out waiting for volume detachment after #{timeout} seconds" + end +end diff --git a/aws/providers/elastic_ip.rb b/aws/providers/elastic_ip.rb new file mode 100644 index 0000000..8429c7b --- /dev/null +++ b/aws/providers/elastic_ip.rb @@ -0,0 +1,83 @@ +include Opscode::Aws::Ec2 + +action :associate do + addr = address(new_resource.ip) + + if addr.nil? + raise "Elastic IP #{new_resource.ip} does not exist" + elsif addr[:instance_id] == instance_id + Chef::Log.debug("Elastic IP #{new_resource.ip} is already attached to the instance") + else + attach(new_resource.ip, new_resource.timeout) + new_resource.updated = true + Chef::Log.info("Attaching Elastic IP #{new_resource.ip} to the instance") + end +end + +action :disassociate do + addr = address(new_resource.ip) + + if addr.nil? + Chef::Log.debug("Elastic IP #{new_resource.ip} does not exist, so there is nothing to detach") + elsif addr[:instance_id] != instance_id + Chef::Log.debug("Elastic IP #{new_resource.ip} is already detached from the instance") + else + Chef::Log.info("Detaching Elastic IP #{new_resource.ip} from the instance") + detach(new_resource.ip, new_resource.timeout) + new_resource.updated = true + end +end + +private + +def address(ip) + ec2.describe_addresses.find{|a| a[:public_ip] == ip} +end + +def attach(ip, timeout) + ec2.associate_address(instance_id, ip) + + # block until attached + begin + Timeout::timeout(timeout) do + while true + addr = address(ip) + if addr.nil? + raise "Elastic IP has been deleted while waiting for attachment" + elsif addr[:instance_id] == instance_id + Chef::Log.debug("Elastic IP is attached to this instance") + break + else + Chef::Log.debug("Elastic IP is currently attached to #{addr[:instance_id]}") + end + sleep 3 + end + end + rescue Timeout::Error + raise "Timed out waiting for attachment after #{timeout} seconds" + end +end + +def detach(ip, timeout) + ec2.disassociate_address(ip) + + # block until detached + begin + Timeout::timeout(timeout) do + while true + addr = address(ip) + if addr.nil? + Chef::Log.debug("Elastic IP has been deleted while waiting for detachment") + elsif addr[:instance_id] != instance_id + Chef::Log.debug("Elastic IP is detached from this instance") + break + else + Chef::Log.debug("Elastic IP is still attached") + end + sleep 3 + end + end + rescue Timeout::Error + raise "Timed out waiting for detachment after #{timeout} seconds" + end +end diff --git a/aws/recipes/default.rb b/aws/recipes/default.rb new file mode 100644 index 0000000..0eef6bf --- /dev/null +++ b/aws/recipes/default.rb @@ -0,0 +1,27 @@ +# +# Cookbook Name:: aws +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +r = gem_package "right_aws" do + action :nothing +end + +r.run_action(:install) + +Gem.clear_paths +require 'right_aws' diff --git a/aws/resources/ebs_volume.rb b/aws/resources/ebs_volume.rb new file mode 100644 index 0000000..5665eb9 --- /dev/null +++ b/aws/resources/ebs_volume.rb @@ -0,0 +1,11 @@ +actions :create, :attach, :detach, :snapshot, :prune + +attribute :aws_access_key, :kind_of => String +attribute :aws_secret_access_key, :kind_of => String +attribute :size, :kind_of => Integer +attribute :snapshot_id, :kind_of => String +attribute :availability_zone, :kind_of => String +attribute :device, :kind_of => String +attribute :volume_id, :kind_of => String +attribute :timeout, :default => 3*60 # 3 mins, nil or 0 for no timeout +attribute :snapshots_to_keep, :default => 2 diff --git a/aws/resources/elastic_ip.rb b/aws/resources/elastic_ip.rb new file mode 100644 index 0000000..9666d66 --- /dev/null +++ b/aws/resources/elastic_ip.rb @@ -0,0 +1,6 @@ +actions :associate, :disassociate + +attribute :aws_access_key, :kind_of => String +attribute :aws_secret_access_key, :kind_of => String +attribute :ip, :kind_of => String +attribute :timeout, :default => 3*60 # 3 mins, nil or 0 for no timeout diff --git a/bluepill/attributes/bluepill.rb b/bluepill/attributes/bluepill.rb new file mode 100755 index 0000000..913cbec --- /dev/null +++ b/bluepill/attributes/bluepill.rb @@ -0,0 +1,4 @@ +default.bluepill[:config_dir] = "/etc/bluepill" +default.bluepill[:log_dir] = "/var/log/bluepill" +default.bluepill[:pid_dir] = "/var/run/bluepill" +bluepill[:version] = "0.0.30" \ No newline at end of file diff --git a/bluepill/definitions/bluepill_monitor.rb b/bluepill/definitions/bluepill_monitor.rb new file mode 100755 index 0000000..2b831bc --- /dev/null +++ b/bluepill/definitions/bluepill_monitor.rb @@ -0,0 +1,21 @@ +define :bluepill_monitor, :enable => true, :rack_config_path => false do + include_recipe "bluepill" + config_path = "#{node[:bluepill][:config_dir]}/#{params[:name]}.conf.rb" + + execute "load-bluepill-#{params[:name]}" do + command "bluepill load #{node[:bluepill][:config_dir]}/#{params[:name]}.conf.rb" + action :nothing + end + + execute "restart-bluepill-#{params[:name]}" do + command "bluepill restart #{params[:name]}" + action :nothing + end + + template config_path do + source params[:source] || "bluepill_#{params[:name]}.conf.erb" + cookbook params[:cookbook] + variables params + notifies :run, resources("execute[load-bluepill-#{params[:name]}]") + end +end \ No newline at end of file diff --git a/bluepill/metadata.json b/bluepill/metadata.json new file mode 100755 index 0000000..26c5896 --- /dev/null +++ b/bluepill/metadata.json @@ -0,0 +1,47 @@ +{ + "replacing": { + + }, + "long_description": "= DESCRIPTION:\n\nInstalls god gem, sets up modular configuration directory and provides a define to monitor processes.\n\n= REQUIREMENTS:\n\n== Platform and Application Environment:\n\nTested on Ubuntu 8.10. May work on other platforms, esp Ubuntu\/Debian. Sample configuration file uses mongrel_runit for managing mongrels via runit. \n\n== Cookbooks:\n\nOpscode cookbooks, http:\/\/github.com\/opscode\/cookbooks\/tree\/master:\n\n* ruby\n* runit\n\nOpscode does not yet have a mongrel_runit cookbook.\n\n= ATTRIBUTES: \n\nNo attributes are used.\n\n= USAGE:\n\nThis recipe is designed to be used through the god_monitor define. Create a god configuration file in your application's cookbook and then call god_monitor:\n\n god_monitor \"myproj\" do\n config \"myproj.god.erb\"\n end\n\nA sample mongrel.god.erb is provided, though it assumes mongrel_runit is used. This can be used as a baseline for customization.\n\n= LICENSE and AUTHOR:\n \nAuthor:: Joshua Timberman ()\nCopyright:: 2009, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "attributes": { + + }, + "maintainer": "Opscode, Inc.", + "recommendations": { + + }, + "license": "Apache 2.0", + "recipes": { + "god::apps": "", + "god": "" + }, + "maintainer_email": "cookbooks@opscode.com", + "suggestions": { + + }, + "dependencies": { + "runit": [ + + ], + "ruby": [ + + ] + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Installs and configures god and provides a define for monitoring", + "version": "0.7.0", + "name": "god", + "providing": { + "god::apps": [ + + ], + "god": [ + + ] + } +} \ No newline at end of file diff --git a/bluepill/metadata.rb b/bluepill/metadata.rb new file mode 100755 index 0000000..eeec383 --- /dev/null +++ b/bluepill/metadata.rb @@ -0,0 +1,5 @@ +maintainer "Joshua Sierles" +maintainer_email "joshua@37signals.com" +license "Apache 2.0" +description "Installs bluepill and provides definitions and templates for monitoring" +version "0.7" \ No newline at end of file diff --git a/bluepill/recipes/default.rb b/bluepill/recipes/default.rb new file mode 100755 index 0000000..fd84922 --- /dev/null +++ b/bluepill/recipes/default.rb @@ -0,0 +1,27 @@ +gem_package "bluepill" do + version node[:bluepill][:version] +end + +directory node[:bluepill][:config_dir] do + owner "root" + group "root" +end + +directory node[:bluepill][:log_dir] do + owner "root" + group "root" +end + +directory node[:bluepill][:pid_dir] do + owner "root" + group "root" +end + +template "/etc/init.d/bluepill" do + source "init.sh.erb" + mode 0755 +end + +service "bluepill" do + action [:enable, :start] +end diff --git a/bluepill/templates/default/init.sh.erb b/bluepill/templates/default/init.sh.erb new file mode 100755 index 0000000..6e1c7c6 --- /dev/null +++ b/bluepill/templates/default/init.sh.erb @@ -0,0 +1,35 @@ +#!/bin/sh + +### BEGIN INIT INFO +# Provides: bluepill +# Required-Start: +# Required-Stop: +# Default-Start: 2 3 4 5 +# Default-Stop: 1 +# Short-Description: Bluepill initializer +### END INIT INFO + +. /lib/lsb/init-functions + +case "$1" in + start) + log_daemon_msg "Starting bluepill services" + for i in <%= @node[:bluepill][:config_dir] %>/*conf.rb; do + <%= @node[:ruby][:bin_dir] %>/bluepill load $i + done + log_end_msg 0 + ;; + stop) + echo "Stop is not implemented, since bluepill processes are run independently." + ;; + restart) + for i in <%= @node[:bluepill][:config_dir] %>/*conf.rb; do + <%= @node[:ruby][:bin_dir] %>/bluepill load $i + done + ;; + *) + log_action_msg "Usage: /etc/init.d/bluepill start" + exit +esac + +exit 0 \ No newline at end of file diff --git a/boost/README.rdoc b/boost/README.rdoc new file mode 100644 index 0000000..1edfb9b --- /dev/null +++ b/boost/README.rdoc @@ -0,0 +1,33 @@ += DESCRIPTION: + +Installs boost, mainly to support Thrift. + += REQUIREMENTS: + +Platform: Ubuntu 9.04. Not tested on any others at this time. + += USAGE: + +Include this recipe to install boost development packages. + + include_recipe "boost" + +Merely installs the libboost-dev package which should grab a bunch of dependencies and get the right thing. + += LICENSE and AUTHOR: + + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/boost/metadata.json b/boost/metadata.json new file mode 100644 index 0000000..c875a8d --- /dev/null +++ b/boost/metadata.json @@ -0,0 +1,40 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs libboost", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "boost": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ] + }, + "version": "0.1.0", + "name": "boost", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "boost": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nInstalls boost, mainly to support Thrift.\n\n= REQUIREMENTS:\n\nPlatform: Ubuntu 9.04. Not tested on any others at this time.\n\n= USAGE:\n\nInclude this recipe to install boost development packages.\n\n include_recipe \"boost\"\n\nMerely installs the libboost-dev package which should grab a bunch of dependencies and get the right thing.\n\n= LICENSE and AUTHOR:\n\n\nAuthor:: Joshua Timberman ()\nCopyright:: 2009, Opscode, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/boost/metadata.rb b/boost/metadata.rb new file mode 100644 index 0000000..43bd644 --- /dev/null +++ b/boost/metadata.rb @@ -0,0 +1,8 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs libboost" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.1" + +supports "ubuntu" diff --git a/boost/recipes/default.rb b/boost/recipes/default.rb new file mode 100644 index 0000000..5cf2f81 --- /dev/null +++ b/boost/recipes/default.rb @@ -0,0 +1,19 @@ +# +# Cookbook Name:: boost +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package "libboost-dev" diff --git a/bootstrap/README.rdoc b/bootstrap/README.rdoc new file mode 100644 index 0000000..00548b2 --- /dev/null +++ b/bootstrap/README.rdoc @@ -0,0 +1,165 @@ += DESCRIPTION: + +This cookbook bootstraps a Chef client or server when Chef is installed via RubyGems. If installing Chef from OS distribution packages, please see the 'chef' cookbook. + += REQUIREMENTS: + +This cookbook requires Chef installed from RubyGems. Chef v0.7.10, for attribute syntax. + +== Platform: + +Server bootstrap is tested on Ubuntu 9.10, 9.04, 8.10 and 8.04, Debian 5.0. + +Client bootstrap is tested on the above, plus CentOS 5.3, Fedora 10, OpenBSD 4.6, FreeBSD 7.1 and Gentoo. OpenSolaris 11 is also tested, but there's a bug in Ohai that requires some manual intervention (OHAI-122). + +== Cookbooks: + +Opscode cookbooks, http://github.com/opscode/cookbooks: + +Both clients and servers: + +* runit + +Servers only: + +* couchdb +* stompserver + +The couchdb and stompserver recipes may be somewhat naive depending on the platform. You should view them online at the github repository to see if your platform is supported. If not, you'll need to manually install them, and remove the "include_recipe" statements from the bootstrap::server recipe. + += ATTRIBUTES: + +Attributes are under 'bootstrap[:chef]' - eg: 'bootstrap[:chef][:client_version]'. You may wish to change some of these locations to customize for your environment. For the bootstrap process this is done with a JSON data file passed to chef-solo. + +== url_type + +Set up the URLs the client should connect to with this. Default is 'http', which tells the client to connect to 'http://server:4000'. If you set up your chef-server to use an SSL front-end, set this to 'https' and the URLs will be 'https://server/'. + +== init_style + +Specifies the init style to use. Default 'runit'. Other possible values 'init', 'bsd', any other string will be treated as unknown. + +If your platform doesn't have a 'runit' package or if the cookbook doesn't detect it, but you stil want to use runit, set init_style to 'none' and install runit separately. + +== path + +This is the base location where chef will store its associated data. Default '/srv/chef' for RubyGems installed systems. The location preference varies by platform. The default is a filesystem hiearchy standard suggestion[1]. Some other locations you may consider, by platform: + +Debian and Red Hat based Linux distros (Ubuntu, CentOS, Fedora, etc): + +* /var/lib/chef + +Any BSD and Gentoo: + +* /var/chef + +== run_path + +Location for pidfiles on systems using init scripts. Default '/var/run/chef'. + +If init_style is 'init', this is used, and should match what the init script itself uses for the PID files. + +== cache_path + +Location where the client will cache cookbooks and other data. Default is 'cache' underneath the bootstrap[:chef][:path] location. Some Linux distributions might prefer /var/cache/chef instead. + +== serve_path + +Used by the Chef server as the base location to "serve" cookbooks, roles and other assets. Default is /srv/chef. + +== server_version, client_version + +Set the version of the Gems to install. This can be used to upgrade Chef automatically[0]. The chef gems are not managed by the Opscode Chef cookbook, however. + +== client_interval + +Number of seconds to run chef-client periodically. Default '1800' (30 minutes). + +== client_splay + +Splay interval to randomly add to interval. Default '20'. + +== log_dir + +Directory where logs are stored if logs are not sent to STDOUT. Systems using runit should send logs to STDOUT as runit manages log output. Default '/var/log/chef'. + +== client_log, indexer_log, server_log + +Location of the client, indexer and server logs, respectively. Default 'STDOUT' on systems with runit, '/var/log/chef/{client,indexer,server}.log' on other systems. + +== server_fqdn + +Fully qualified domain name of the server. Default is the current node's fqdn as detected by Ohai. For clients, set this to the hostname of your environment's Chef Server. + +== server_token + +The validation_token used to automatically authorize chef-clients. Default is a random string generated every time chef-solo runs. Use chef-client -t 'validation_token' to automatically validate the client. + +[0] http://blog.opscode.com/2009/08/cool-chef-tricks-upgrade-chef-with-chef.html +[1] http://www.pathname.com/fhs/ + += USAGE: + +Opscode stores this cookbook and some others (see the requirements above) on S3. Use chef-solo: + + sudo chef-solo -j chef.json -c solo.rb -r http://s3.amazonaws.com/chef-solo/bootstrap-latest.tar.gz + +You set the attributes through the chef.json file, and tell Solo where to put them with solo.rb. + +== Clients: + +Common attributes you may wish to adjust for the client: + +{ + "bootstrap": { + "chef": { + "url_type": "http", + "init_style": "runit", + "path": "/srv/chef", + "server_fqdn": "localhost.localdomain", + } + }, + "recipes": "bootstrap::client" +} + +== Servers: + +Common attributes you may wish to adjust for the server: + +{ + "bootstrap": { + "chef": { + "url_type": "http", + "init_style": "runit", + "path": "/srv/chef", + "serve_path": "/srv/chef", + "server_fqdn": "localhost.localdomain", + } + }, + "recipes": "bootstrap::server" +} + +Note that the server recipe includes the client recipe as well, since we recommend managing the chef-server with Chef. + +For more information on usage and next steps, please see the Chef wiki. + + http://wiki.opscode.com/display/chef/Home + += LICENSE and AUTHOR: + +Author:: Joshua Timberman +Author:: Joshua Sierles + +Copyright 2008-2009, Opscode, Inc +Copyright 2009, 37signals + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and diff --git a/bootstrap/attributes/bootstrap.rb b/bootstrap/attributes/bootstrap.rb new file mode 100644 index 0000000..00024ac --- /dev/null +++ b/bootstrap/attributes/bootstrap.rb @@ -0,0 +1,50 @@ +# +# Cookbook Name:: bootstrap +# Attributes:: bootstrap +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +validation_token = "" +chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a +20.times { |i| validation_token << chars[rand(chars.size-1)] } + +set_unless[:bootstrap][:chef][:umask] = 0022 +set_unless[:bootstrap][:chef][:url_type] = "http" +set_unless[:bootstrap][:chef][:init_style] = "runit" +set_unless[:bootstrap][:chef][:path] = "/srv/chef" +set_unless[:bootstrap][:chef][:run_path] = "/var/run/chef" +set_unless[:bootstrap][:chef][:cache_path] = "/#{bootstrap[:chef][:path]}/cache" +set_unless[:bootstrap][:chef][:serve_path] = "/srv/chef" + +set_unless[:bootstrap][:chef][:server_version] = "0.7.16" +set_unless[:bootstrap][:chef][:client_version] = "0.7.16" +set_unless[:bootstrap][:chef][:client_interval] = "1800" +set_unless[:bootstrap][:chef][:client_splay] = "20" +set_unless[:bootstrap][:chef][:log_dir] = "/var/log/chef" + +case bootstrap[:chef][:init_style] +when "runit" + set_unless[:bootstrap][:chef][:client_log] = "STDOUT" + set_unless[:bootstrap][:chef][:server_log] = "STDOUT" + set_unless[:bootstrap][:chef][:indexer_log] = "STDOUT" +else + set_unless[:bootstrap][:chef][:client_log] = "#{bootstrap[:chef][:log_dir]}/client.log" + set_unless[:bootstrap][:chef][:server_log] = "#{bootstrap[:chef][:log_dir]}/server.log" + set_unless[:bootstrap][:chef][:indexer_log] = "#{bootstrap[:chef][:log_dir]}/indexer.log" +end + +set_unless[:bootstrap][:chef][:server_fqdn] = domain ? "chef.#{domain}" : "chef" +set_unless[:bootstrap][:chef][:server_token] = validation_token diff --git a/bootstrap/metadata.json b/bootstrap/metadata.json new file mode 100644 index 0000000..e31efa7 --- /dev/null +++ b/bootstrap/metadata.json @@ -0,0 +1,77 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Configures RubyGems-installed Chef", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "bootstrap::client": "", + "bootstrap": "", + "bootstrap::server": "" + }, + "suggestions": { + + }, + "platforms": { + "freebsd": [ + + ], + "ubuntu": [ + + ], + "openbsd": [ + + ], + "fedora": [ + + ], + "centos": [ + + ], + "redhat": [ + + ], + "debian": [ + + ] + }, + "version": "0.1.0", + "name": "bootstrap", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "bootstrap": [ + + ], + "bootstrap::client": [ + + ], + "bootstrap::server": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nThis cookbook bootstraps a Chef client or server when Chef is installed via RubyGems. If installing Chef from OS distribution packages, please see the 'chef' cookbook. \n\n= REQUIREMENTS:\n\nThis cookbook requires Chef installed from RubyGems. Chef v0.7.10, for attribute syntax.\n\n== Platform:\n\nServer bootstrap is tested on Ubuntu 9.10, 9.04, 8.10 and 8.04, Debian 5.0.\n\nClient bootstrap is tested on the above, plus CentOS 5.3, Fedora 10, OpenBSD 4.6, FreeBSD 7.1 and Gentoo. OpenSolaris 11 is also tested, but there's a bug in Ohai that requires some manual intervention (OHAI-122).\n\n== Cookbooks:\n\nOpscode cookbooks, http:\/\/github.com\/opscode\/cookbooks:\n\nBoth clients and servers:\n\n* runit\n\nServers only:\n\n* couchdb\n* stompserver\n\nThe couchdb and stompserver recipes may be somewhat naive depending on the platform. You should view them online at the github repository to see if your platform is supported. If not, you'll need to manually install them, and remove the \"include_recipe\" statements from the bootstrap::server recipe.\n\n= ATTRIBUTES:\n\nAttributes are under 'bootstrap[:chef]' - eg: 'bootstrap[:chef][:client_version]'. You may wish to change some of these locations to customize for your environment. For the bootstrap process this is done with a JSON data file passed to chef-solo.\n\n== url_type\n\nSet up the URLs the client should connect to with this. Default is 'http', which tells the client to connect to 'http:\/\/server:4000'. If you set up your chef-server to use an SSL front-end, set this to 'https' and the URLs will be 'https:\/\/server\/'. \n\n== init_style\n\nSpecifies the init style to use. Default 'runit'. Other possible values 'init', 'bsd', any other string will be treated as unknown.\n\nIf your platform doesn't have a 'runit' package or if the cookbook doesn't detect it, but you stil want to use runit, set init_style to 'none' and install runit separately.\n\n== path\n\nThis is the base location where chef will store its associated data. Default '\/srv\/chef' for RubyGems installed systems. The location preference varies by platform. The default is a filesystem hiearchy standard suggestion[1]. Some other locations you may consider, by platform:\n\nDebian and Red Hat based Linux distros (Ubuntu, CentOS, Fedora, etc):\n\n* \/var\/lib\/chef\n\nAny BSD and Gentoo:\n\n* \/var\/chef\n\n== run_path\n\nLocation for pidfiles on systems using init scripts. Default '\/var\/run\/chef'.\n\nIf init_style is 'init', this is used, and should match what the init script itself uses for the PID files.\n\n== cache_path\n\nLocation where the client will cache cookbooks and other data. Default is 'cache' underneath the bootstrap[:chef][:path] location. Some Linux distributions might prefer \/var\/cache\/chef instead.\n\n== serve_path\n\nUsed by the Chef server as the base location to \"serve\" cookbooks, roles and other assets. Default is \/srv\/chef.\n\n== server_version, client_version\n\nSet the version of the Gems to install. This can be used to upgrade Chef automatically[0]. The chef gems are not managed by the Opscode Chef cookbook, however.\n\n== client_interval\n\nNumber of seconds to run chef-client periodically. Default '1800' (30 minutes).\n\n== client_splay\n\nSplay interval to randomly add to interval. Default '20'.\n\n== log_dir\n\nDirectory where logs are stored if logs are not sent to STDOUT. Systems using runit should send logs to STDOUT as runit manages log output. Default '\/var\/log\/chef'.\n\n== client_log, indexer_log, server_log\n\nLocation of the client, indexer and server logs, respectively. Default 'STDOUT' on systems with runit, '\/var\/log\/chef\/{client,indexer,server}.log' on other systems.\n\n== server_fqdn\n\nFully qualified domain name of the server. Default is the current node's fqdn as detected by Ohai. For clients, set this to the hostname of your environment's Chef Server.\n\n== server_token\n\nThe validation_token used to automatically authorize chef-clients. Default is a random string generated every time chef-solo runs. Use chef-client -t 'validation_token' to automatically validate the client.\n\n[0] http:\/\/blog.opscode.com\/2009\/08\/cool-chef-tricks-upgrade-chef-with-chef.html\n[1] http:\/\/www.pathname.com\/fhs\/\n\n= USAGE:\n\nOpscode stores this cookbook and some others (see the requirements above) on S3. Use chef-solo:\n\n sudo chef-solo -j chef.json -c solo.rb -r http:\/\/s3.amazonaws.com\/chef-solo\/bootstrap-latest.tar.gz\n\nYou set the attributes through the chef.json file, and tell Solo where to put them with solo.rb.\n\n== Clients:\n\nCommon attributes you may wish to adjust for the client:\n\n{\n \"bootstrap\": {\n \"chef\": {\n \"url_type\": \"http\",\n \"init_style\": \"runit\",\n \"path\": \"\/srv\/chef\",\n \"server_fqdn\": \"localhost.localdomain\",\n }\n },\n \"recipes\": \"bootstrap::client\"\n}\n\n== Servers:\n\nCommon attributes you may wish to adjust for the server:\n\n{\n \"bootstrap\": {\n \"chef\": {\n \"url_type\": \"http\",\n \"init_style\": \"runit\",\n \"path\": \"\/srv\/chef\",\n \"serve_path\": \"\/srv\/chef\",\n \"server_fqdn\": \"localhost.localdomain\",\n }\n },\n \"recipes\": \"bootstrap::server\"\n}\n\nNote that the server recipe includes the client recipe as well, since we recommend managing the chef-server with Chef.\n\nFor more information on usage and next steps, please see the Chef wiki.\n\n http:\/\/wiki.opscode.com\/display\/chef\/Home\n\n= LICENSE and AUTHOR:\n\nAuthor:: Joshua Timberman \nAuthor:: Joshua Sierles \n\nCopyright 2008-2009, Opscode, Inc\nCopyright 2009, 37signals\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\n", + "replacing": { + + }, + "dependencies": { + "stompserver": [ + + ], + "runit": [ + + ], + "couchdb": [ + + ], + "apache2": [ + + ] + } +} \ No newline at end of file diff --git a/bootstrap/metadata.rb b/bootstrap/metadata.rb new file mode 100644 index 0000000..f24a020 --- /dev/null +++ b/bootstrap/metadata.rb @@ -0,0 +1,14 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Configures RubyGems-installed Chef" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.2" + +%w{ ubuntu debian redhat centos fedora freebsd openbsd }.each do |os| + supports os +end + +%w{ runit couchdb stompserver apache2 }.each do |cb| + depends cb +end diff --git a/bootstrap/recipes/client.rb b/bootstrap/recipes/client.rb new file mode 100644 index 0000000..4caf821 --- /dev/null +++ b/bootstrap/recipes/client.rb @@ -0,0 +1,87 @@ +# +# Cookbook Name:: bootstrap +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +root_group = value_for_platform( + "openbsd" => { "default" => "wheel" }, + "freebsd" => { "default" => "wheel" }, + "default" => "root" +) + +gem_package "chef" do + version node[:bootstrap][:chef][:client_version] +end + +case node[:bootstrap][:chef][:init_style] +when "runit" + client_log = node[:bootstrap][:chef][:client_log] + show_time = "false" + include_recipe "runit" + runit_service "chef-client" +when "init" + client_log = "\"#{node[:bootstrap][:chef][:client_log]}\"" + show_time = "true" + + directory node[:bootstrap][:chef][:run_path] do + action :create + owner "root" + group root_group + mode "755" + end + + service "chef-client" do + action :nothing + end + + Chef::Log.info("You specified service style 'init'.") + Chef::Log.info("'init' scripts available in #{node[:languages][:ruby][:gems_dir]}/gems/chef-#{node[:bootstrap][:chef][:client_version]}/distro") +when "bsd" + client_log = node[:bootstrap][:chef][:client_log] + show_time = "false" + Chef::Log.info("You specified service style 'bsd'. You will need to set up your rc.local file.") + Chef::Log.info("Hint: chef-client -i #{node[:bootstrap][:chef][:client_interval]} -s #{node[:bootstrap][:chef][:client_splay]}") +else + client_log = node[:bootstrap][:chef][:client_log] + show_time = "false" + Chef::Log.info("Could not determine service init style, manual intervention required to start up the client service.") +end + +chef_dirs = [ + node[:bootstrap][:chef][:log_dir], + node[:bootstrap][:chef][:path], + "/etc/chef" +] + +chef_dirs.each do |dir| + directory dir do + owner "root" + group root_group + mode "755" + end +end + +template "/etc/chef/client.rb" do + source "client.rb.erb" + owner "root" + group root_group + mode "644" + variables( + :client_log => client_log, + :show_time => show_time + ) +end diff --git a/bootstrap/recipes/default.rb b/bootstrap/recipes/default.rb new file mode 100644 index 0000000..3f4ba70 --- /dev/null +++ b/bootstrap/recipes/default.rb @@ -0,0 +1,18 @@ +# +# Cookbook Name:: bootstrap +# Recipe:: client +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/bootstrap/recipes/server.rb b/bootstrap/recipes/server.rb new file mode 100644 index 0000000..19ff7b1 --- /dev/null +++ b/bootstrap/recipes/server.rb @@ -0,0 +1,120 @@ +# +# Author:: Joshua Timberman +# Author:: Joshua Sierles +# +# Cookbook Name:: bootstrap +# Recipe:: server +# +# Copyright 2009, Opscode, Inc. +# Copyright 2009, 37signals +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +root_group = value_for_platform( + "openbsd" => { "default" => "wheel" }, + "freebsd" => { "default" => "wheel" }, + "default" => "root" +) + +include_recipe "bootstrap::client" +include_recipe "stompserver" + +case node[:platform] +when "ubuntu" + if node[:platform_version].to_f >= 8.10 + include_recipe "couchdb" + end +when "debian" + if node[:platform_version].to_f >= 5.0 || node[:platform_version] =~ /.*sid/ + include_recipe "couchdb" + end +when "centos","redhat","fedora" + include_recipe "couchdb" +else + Chef::Log.info("Unknown platform for CouchDB. Manual installation of CouchDB required.") +end + +%w{ chef-server chef-server-slice }.each do |gem| + gem_package gem do + version node[:bootstrap][:chef][:server_version] + end +end + +if node[:bootstrap][:chef][:server_log] == "STDOUT" + server_log = node[:bootstrap][:chef][:server_log] + show_time = "false" +else + server_log = "\"#{node[:bootstrap][:chef][:server_log]}\"" + indexer_log = "\"#{node[:bootstrap][:chef][:indexer_log]}\"" + show_time = "true" +end + +template "/etc/chef/server.rb" do + source "server.rb.erb" + owner "root" + group root_group + mode "600" + variables( + :server_log => server_log, + :show_time => show_time + ) +end + +%w{ openid cache search_index openid/cstore openid/store }.each do |dir| + directory "#{node[:bootstrap][:chef][:path]}/#{dir}" do + owner "root" + group root_group + mode "755" + end +end + +directory "/etc/chef/certificates" do + owner "root" + group root_group + mode "700" +end + +directory node[:bootstrap][:chef][:run_path] do + owner "root" + group root_group + mode "755" +end + +case node[:bootstrap][:chef][:init_style] +when "runit" + include_recipe "runit" + runit_service "chef-indexer" + runit_service "chef-server" + service "chef-server" do + restart_command "sv int chef-server" + end +when "init" + show_time = "true" + + service "chef-indexer" do + action :nothing + end + + service "chef-server" do + action :nothing + end + + Chef::Log.info("You specified service style 'init'.") + Chef::Log.info("'init' scripts available in #{node[:languages][:ruby][:gems_dir]}/gems/chef-#{node[:bootstrap][:chef][:client_version]}/distro") +when "bsd" + Chef::Log.info("You specified service style 'bsd'. You will need to set up your rc.local file for chef-indexer and chef-server.") + Chef::Log.info("Server startup command: chef-server -c2 -d") +else + Chef::Log.info("Could not determine service init style, manual intervention required to set up indexer and server services.") +end diff --git a/bootstrap/templates/default/client.rb.erb b/bootstrap/templates/default/client.rb.erb new file mode 100644 index 0000000..a719708 --- /dev/null +++ b/bootstrap/templates/default/client.rb.erb @@ -0,0 +1,30 @@ +# +# Chef Client Config File +# +# Dynamically generated by Chef - local modifications will be replaced +# + +log_level :info +log_location <%= @client_log %> +ssl_verify_mode :verify_none +<% if @node[:bootstrap][:chef][:url_type] == "https" -%> +registration_url "https://<%= @node[:bootstrap][:chef][:server_fqdn] %>" +openid_url "https://<%= @node[:bootstrap][:chef][:server_fqdn] %>:444" +template_url "https://<%= @node[:bootstrap][:chef][:server_fqdn] %>" +remotefile_url "https://<%= @node[:bootstrap][:chef][:server_fqdn] %>" +search_url "https://<%= @node[:bootstrap][:chef][:server_fqdn] %>" +role_url "https://<%= @node[:bootstrap][:chef][:server_fqdn] %>" +<% else -%> +registration_url "http://<%= @node[:bootstrap][:chef][:server_fqdn] %>:4000" +openid_url "http://<%= @node[:bootstrap][:chef][:server_fqdn] %>:4001" +template_url "http://<%= @node[:bootstrap][:chef][:server_fqdn] %>:4000" +remotefile_url "http://<%= @node[:bootstrap][:chef][:server_fqdn] %>:4000" +search_url "http://<%= @node[:bootstrap][:chef][:server_fqdn] %>:4000" +role_url "http://<%= @node[:bootstrap][:chef][:server_fqdn] %>:4000" +<% end -%> + +file_cache_path "<%= @node[:bootstrap][:chef][:cache_path] %>" + +pid_file "<%= @node[:bootstrap][:chef][:run_path] %>/client.pid" + +Chef::Log::Formatter.show_time = <%= @show_time %> diff --git a/bootstrap/templates/default/server.rb.erb b/bootstrap/templates/default/server.rb.erb new file mode 100644 index 0000000..941e3d8 --- /dev/null +++ b/bootstrap/templates/default/server.rb.erb @@ -0,0 +1,34 @@ +# +# Chef Server Config File +# +# Dynamically generated by Chef - local modifications will be replaced + +log_level :info +log_location <%= @server_log %> +ssl_verify_mode :verify_none +registration_url "http://<%= @node[:bootstrap][:chef][:server_fqdn] %>:4000" +openid_url "http://<%= @node[:bootstrap][:chef][:server_fqdn] %>:4001" +template_url "http://<%= @node[:bootstrap][:chef][:server_fqdn] %>:4000" +remotefile_url "http://<%= @node[:bootstrap][:chef][:server_fqdn] %>:4000" +search_url "http://<%= @node[:bootstrap][:chef][:server_fqdn] %>:4000" +role_url "http://<%= @node[:bootstrap][:chef][:server_fqdn] %>:4000" + +validation_token "<%= @node[:bootstrap][:chef][:server_token] %>" + +cookbook_path [ "<%= @node[:bootstrap][:chef][:serve_path] %>/site-cookbooks", "<%= @node[:bootstrap][:chef][:serve_path] %>/cookbooks" ] + +file_cache_path "<%= @node[:bootstrap][:chef][:cache_path] %>" +node_path "<%= @node[:bootstrap][:chef][:serve_path] %>/nodes" +openid_store_path "<%= @node[:bootstrap][:chef][:path] %>/openid/store" +openid_cstore_path "<%= @node[:bootstrap][:chef][:path] %>/openid/cstore" +search_index_path "<%= @node[:bootstrap][:chef][:path] %>/search_index" +role_path "<%= @node[:bootstrap][:chef][:serve_path] %>/roles" + +umask <%= @node[:bootstrap][:chef][:umask] %> + +# See http://wiki.opscode.com/display/chef/Securing+Chef+Server +# For more information on these settings. +#authorized_openid_providers [ "https://<%= @node[:bootstrap][:chef][:server_fqdn]%>", "https://chef", "myopenid.com" ] +#authorized_openid_identifiers [ "" ] + +Chef::Log::Formatter.show_time = <%= @show_time %> diff --git a/bootstrap/templates/default/sv-chef-client-log-run.erb b/bootstrap/templates/default/sv-chef-client-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/bootstrap/templates/default/sv-chef-client-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/bootstrap/templates/default/sv-chef-client-run.erb b/bootstrap/templates/default/sv-chef-client-run.erb new file mode 100644 index 0000000..7c0dec7 --- /dev/null +++ b/bootstrap/templates/default/sv-chef-client-run.erb @@ -0,0 +1,4 @@ +#!/bin/sh +PATH=/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin<% if @node[:languages][:ruby][:gems_dir] %>:<%= @node[:languages][:ruby][:gems_dir] %>/bin<% end -%> +exec 2>&1 +exec /usr/bin/env chef-client -i <%= @node[:bootstrap][:chef][:client_interval] %> -s <%= @node[:bootstrap][:chef][:client_splay] %> <% if @node[:bootstrap][:chef][:client_log] != "STDOUT" then -%>-L <%= @node[:bootstrap][:chef][:client_log] %><% end -%> diff --git a/bootstrap/templates/default/sv-chef-indexer-log-run.erb b/bootstrap/templates/default/sv-chef-indexer-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/bootstrap/templates/default/sv-chef-indexer-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/bootstrap/templates/default/sv-chef-indexer-run.erb b/bootstrap/templates/default/sv-chef-indexer-run.erb new file mode 100644 index 0000000..634f651 --- /dev/null +++ b/bootstrap/templates/default/sv-chef-indexer-run.erb @@ -0,0 +1,4 @@ +#!/bin/sh +PATH=/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin<% if @node[:languages][:ruby][:gems_dir] %>:<%= @node[:languages][:ruby][:gems_dir] %>/bin<% end -%> +exec 2>&1 +exec /usr/bin/env chef-indexer diff --git a/bootstrap/templates/default/sv-chef-server-log-run.erb b/bootstrap/templates/default/sv-chef-server-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/bootstrap/templates/default/sv-chef-server-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/bootstrap/templates/default/sv-chef-server-run.erb b/bootstrap/templates/default/sv-chef-server-run.erb new file mode 100644 index 0000000..7d20fe0 --- /dev/null +++ b/bootstrap/templates/default/sv-chef-server-run.erb @@ -0,0 +1,4 @@ +#!/bin/sh +PATH=/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin<% if @node[:languages][:ruby][:gems_dir] %>:<%= @node[:languages][:ruby][:gems_dir] %>/bin<% end -%> +exec 2>&1 +exec /usr/bin/env chef-server -N -c2 -P <%= @node[:bootstrap][:chef][:run_path] %>/server.%s.pid diff --git a/build-essential/metadata.json b/build-essential/metadata.json new file mode 100644 index 0000000..38e9f0b --- /dev/null +++ b/build-essential/metadata.json @@ -0,0 +1,46 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs C compiler \/ build tools", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "build-essential": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "centos": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "build-essential", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "build-essential": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/build-essential/metadata.rb b/build-essential/metadata.rb new file mode 100644 index 0000000..5e1613e --- /dev/null +++ b/build-essential/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs C compiler / build tools" +version "0.7" + +%w{ centos ubuntu debian }.each do |os| + supports os +end diff --git a/build-essential/recipes/default.rb b/build-essential/recipes/default.rb new file mode 100644 index 0000000..b4dcce0 --- /dev/null +++ b/build-essential/recipes/default.rb @@ -0,0 +1,43 @@ +# +# Cookbook Name:: build-essential +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node[:platform] +when "ubuntu","debian" + %w{build-essential binutils-doc}.each do |pkg| + package pkg do + action :install + end + end +when "centos" + package "gcc" do + action :install + end +end + +package "autoconf" do + action :install +end + +package "flex" do + action :install +end + +package "bison" do + action :install +end diff --git a/build/attributes/build.rb b/build/attributes/build.rb new file mode 100755 index 0000000..13fa538 --- /dev/null +++ b/build/attributes/build.rb @@ -0,0 +1,2 @@ +build Mash.new unless attribute?("build") +build[:ruby_enterprise_edition] = {:packages => ["libssl-dev", "libreadline5-dev", "bison"]} diff --git a/build/metadata.json b/build/metadata.json new file mode 100755 index 0000000..bf8bdb1 --- /dev/null +++ b/build/metadata.json @@ -0,0 +1,38 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "build": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures build dependencies", + "version": "0.1.0", + "name": "build", + "providing": { + "build": [ + + ] + } +} \ No newline at end of file diff --git a/build/metadata.rb b/build/metadata.rb new file mode 100755 index 0000000..00bb229 --- /dev/null +++ b/build/metadata.rb @@ -0,0 +1,4 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures build dependencies" +version "0.1" \ No newline at end of file diff --git a/build/recipes/default.rb b/build/recipes/default.rb new file mode 100755 index 0000000..e4a30b6 --- /dev/null +++ b/build/recipes/default.rb @@ -0,0 +1,41 @@ +case node[:platform] +when "ubuntu","debian" + %w{build-essential binutils-doc}.each do |pkg| + package pkg do + action :install + end + end +when "centos" + package "gcc" do + action :install + end +end + +package "autoconf" do + action :install +end + +package "flex" do + action :install +end + +package "bison" do + action :install +end + +gem_package "git_remote_branch" + +directory "/usr/local/build" do + action :create + owner "root" + group "admin" + mode 0775 +end + +if node[:build] + node[:build].each do |name, config| + config[:packages].each do |pack| + package pack + end + end +end \ No newline at end of file diff --git a/capistrano/definitions/cap_setup.rb b/capistrano/definitions/cap_setup.rb new file mode 100644 index 0000000..997062a --- /dev/null +++ b/capistrano/definitions/cap_setup.rb @@ -0,0 +1,46 @@ +# +# Cookbook Name:: capistrano +# Definition:: cap_setup +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :cap_setup, :path => nil, :owner => "root", :group => "root", :appowner => "nobody" do + include_recipe "capistrano" + + directory params[:path] do + owner params[:owner] + group params[:group] + mode 0755 + end + + # after chef-174 fixed, change mode to 2775 + %w{ releases shared }.each do |dir| + directory "#{params[:path]}/#{dir}" do + owner params[:owner] + group params[:group] + mode 0775 + end + end + + %w{ log system }.each do |dir| + directory "#{params[:path]}/shared/#{dir}" do + owner params[:appowner] + group params[:group] + mode 0775 + end + end + +end diff --git a/capistrano/metadata.json b/capistrano/metadata.json new file mode 100644 index 0000000..3976c89 --- /dev/null +++ b/capistrano/metadata.json @@ -0,0 +1,59 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs Capistrano gem and provides a define to set up deployment for an application", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "capistrano": "", + "capistrano": "Installs Capistrano gem" + }, + "suggestions": { + + }, + "platforms": { + "freebsd": [ + + ], + "ubuntu": [ + + ], + "openbsd": [ + + ], + "fedora": [ + + ], + "centos": [ + + ], + "redhat": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "capistrano", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "capistrano": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/capistrano/metadata.rb b/capistrano/metadata.rb new file mode 100644 index 0000000..040a65f --- /dev/null +++ b/capistrano/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs Capistrano gem and provides a define to set up deployment for an application" +version "0.7" +recipe "capistrano", "Installs Capistrano gem" +%w{ ubuntu debian redhat centos fedora freebsd openbsd }.each do |os| + supports os +end diff --git a/capistrano/recipes/default.rb b/capistrano/recipes/default.rb new file mode 100644 index 0000000..39e27ba --- /dev/null +++ b/capistrano/recipes/default.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: capistrano +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +gem_package "capistrano" diff --git a/chef/README.rdoc b/chef/README.rdoc new file mode 100644 index 0000000..0a21e31 --- /dev/null +++ b/chef/README.rdoc @@ -0,0 +1,221 @@ += IMPORTANT CHANGES: + +First be aware of important changes in this version from previous versions. + +== General: + +The attributes have been consolidated into one file, chef.rb, rather than split amongst chef.rb, client.rb, indexer.rb and server.rb. + +== Client: + +This cookbook no longer manages the chef package version, it manages /etc/chef/client.rb, reloads the configuration using the new ruby_block resource if the template changes. + +The client service is not managed at all. It is assumed to be set up via init script or runit from package installation or bootstrap. + +== Server: + +*This cookbook no longer configures a Chef Server under Passenger by default.* + +The stompserver and couchdb cookbooks are not included by default. See below under Cookbooks requirements. + +The default server recipe (chef::server) sets up two Merb Mongrel workers for the webui/api (port 4000) and openid (port 4001). + +The default server recipe (chef::server), creates but does not manage the chef-indexer and chef-server services, and configures both from /etc/chef/server.rb. Some package installation methods (e.g., Debian) have a separate config file for chef-indexer. + +The chef::server_proxy recipe sets up an Apache proxy vhost to provide SSL in front of the chef-server running as a Merb application. + += DESCRIPTION: + +Use this cookbook to configure a chef client to connect to your preferred chef-server, or config a chef-server. + += REQUIREMENTS: + +Chef v0.7.10, for attribute 'default' syntax. + +== Platform: + +Server is tested on Ubuntu 9.10, 9.04, 8.10 and 8.04, Debian 5.0. + +Client is tested on the above, plus CentOS 5.3, Fedora 10, OpenBSD 4.6, FreeBSD 7.1 and Gentoo. + +== Cookbooks: + +Client: + +runit is suggested for RubyGem installations. Clients do not require any other cookbooks. + +Server: + +couchdb and stompserver are suggested for RubyGem installations. On systems where Chef and dependencies were installed from platform packages, CouchDB and Stompserver should be installed and configured sufficiently. Localised configuration requires additional changes to the server recipe and may require changes when using the Opscode recipes. + +Server using server_proxy: + +* apache2 (opscode/cookbooks) + += ATTRIBUTES: + +*A note about paths:* We try to stick with generally accepted FHS guidelines for path locations, but you might need to adjust these for your platform. See the filesystem hierarchy documentation for your operating system if you're not sure. + +=== url_type + +Set up the URLs the client should connect to with this. Default is 'http', which tells the client to connect to 'http://server:4000'. If you set up your chef-server to use an SSL front-end for example with chef::server_proxy, set this to 'https' and the URLs will be 'https://server/'. + +=== init_style + +Specifies the init style to use. Default 'runit'. Other possible values 'init', 'bsd', any other string will be treated as unknown. + +If your platform doesn't have a 'runit' package or if the cookbook doesn't detect it, but you stil want to use runit, set init_style to 'none' and install runit separately. + +=== path + +This is the base location where chef will store its associated data. Default '/srv/chef' for RubyGems installed systems. The location preference varies by platform. The default is a filesystem hiearchy standard suggestion[1]. Some other locations you may consider, by platform: + +Debian and Red Hat based Linux distros (Ubuntu, CentOS, Fedora, etc): + +* /var/lib/chef + +Any BSD and Gentoo: + +* /var/chef + +=== run_path + +Location for pidfiles on systems using init scripts. Default '/var/run/chef'. + +If init_style is 'init', this is used, and should match what the init script itself uses for the PID files. + +=== cache_path + +Location where the client will cache cookbooks and other data. Default is 'cache' underneath the bootstrap[:chef][:path] location. Linux distributions might prefer /var/cache/chef instead. + +=== serve_path + +Used by the Chef server as the base location to "serve" cookbooks, roles and other assets. Default is /srv/chef. + +=== server_version, client_version + +Set the version Chef. This is now unused in the chef cookbook for any specific configuration but you can optionally override the opscode recipe with one that manages the specific version of Chef installed. Default is the latest Chef release. Informational messages may be printed using the veresion, though. + +=== client_interval + +Number of seconds to run chef-client periodically. Default '1800' (30 minutes). + +=== client_splay + +Splay interval to randomly add to interval. Default '20'. + +=== log_dir + +Directory where logs are stored if logs are not sent to STDOUT. Systems using runit should send logs to STDOUT as runit manages log output. Default STDOUT when init_style is 'runit', otherwise the default is '/var/log/chef'. + +=== client_log, indexer_log, server_log + +Location of the client, indexer and server logs, respectively. Default 'STDOUT' on systems with runit, '/var/log/chef/{client,indexer,server}.log' on other systems. + +=== server_fqdn + +Fully qualified domain name of the server. Default is 'chef.domain' where domain is detected by Ohai. You should configure a DNS entry for your Chef Server. + +On servers, this specifies the URLs the server expects, plus it is used in the server_ssl_req as the canonical name (CN) and in server_proxy for the vhost name. + +On clients, this specifies the URLs the client uses to connect to the server. + +=== server_token + +The validation_token used to automatically authorize chef-clients. Default is a random string generated every time chef-solo runs, and can be stored as a node attribute on the server. Use chef-client -t 'validation_token' to automatically validate the client. + +=== server_ssl_req + +Used by the server_proxy recipe, this attribute can be used to set up a self-signed SSL certificate automatically using openssl. Fields: + +* C: country (two letter code) +* ST: state/province +* L: locality or city +* O: organization +* OU: organizational unit +* CN: canonical name, usually the fully qualified domain name of the server (FQDN) +* emailAddress: contact email address + += USAGE: + +This cookbook is primarily designed to configure a Chef client or server with the /etc/chef/ configuration files. Server services should be restarted when the config file changes. The running client configuration will get reloaded from the template if it changes. + +The primary usage would be to set up a JSON file used with chef-client -j to set the run_list and attributes. The settings could alternately be put in a role, as well. When the JSON is used, node will have the run_list and attributes saved in the Chef Server it connected to. + +Example JSON to set up a client: + + { + "chef": { + "url_type": "https", + "init_style": "init", + "server_fqdn": "chef.example.com" + }, + "recipes": "chef::client" + } + +This will tell the client to use the https style URLs (see chef::client below), that we'll have init scripts set up, and to connect to the server "chef.example.com" + +=== Passenger Not Used: + +As mentioned above, Passenger is no longer used as the default. Use the server_proxy recipe to create an SSL front-end. + +== Server Default (chef::server) + +By default, the server is setup to run as a standard Merb application with the Mongrel adapter, using the package installation or the bootstrap cookbook. The chef::server recipe is used to maintain the configuration. + +When using chef::server only, clients can use the default value for url_type (http). + +== Server Proxy (chef::server_proxy) + +If you would like to set up an SSL front end for your server, use the chef::server_proxy recipe. + +When using this recipe, clients should have the url_type attribute set to "https". + +You will need to edit the server_ssl_request attribute so the certificate is generated correctly. + +The recipe itself will set up the Apache proxy: + +* Add port 444 to the listen_ports (Apache's ports.conf), required for OpenID. +* Enable Apache modules proxy proxy_http proxy_balancer ssl rewrite headers +* Create the SSL certificate based on the server_ssl_req attribute. +* Set up and enable virtual hosts on ports 443 and 444 in the site config "chef_server.conf". + +The proxy will send requests from port 443 to the Mongrel running on port 4000 (webui/api) and requests on port 444 to the Mongrel on port 4001 (openid). Be sure to adjust any firewall rules or security group settings appropriately for these ports (4000, 4001, 443, 444). + +=== SSL Certificates + +The server_proxy recipe will generate a self-signed PEM certificate on the first run. If you use opscode's chef-repo, use rake to generate your own site-specific certificate if you wish. You can also use a purchased certificate to replace the one generated through this cookbook, but it must be named by the fully qualified domain name as used in the server_fqdn attribute. + +== Client Default (chef::client) + +If your Chef Server's fully qualified domain name is not "chef.domain" where domain is the node attribute detected by ohai, then you'll need to specify the server_fqdn attribute for your clients. + +You may want to adjust the path attributes as described above. + +Make sure you specify the correct url_type for your Chef Server. This will create the URLs in the client config file as so: + +http:: http://chef.domain:4000/ + +https:: https://chef.domain/ + +(the openid_url will be :4001 and :444 respectively.) + += LICENSE and AUTHOR: + +Author:: Joshua Timberman +Author:: Joshua Sierles + +Copyright 2008-2009, Opscode, Inc +Copyright 2009, 37signals + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chef/attributes/chef.rb b/chef/attributes/chef.rb new file mode 100644 index 0000000..7f24af8 --- /dev/null +++ b/chef/attributes/chef.rb @@ -0,0 +1,61 @@ +# +# Author:: Joshua Timberman +# Cookbook Name:: chef +# Attributes:: chef +# +# Copyright 2008-2009, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +validation_token = "" +chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a +20.times { |i| validation_token << chars[rand(chars.size-1)] } + +set_unless[:chef][:umask] = 0022 +set_unless[:chef][:url_type] = "http" +set_unless[:chef][:init_style] = "runit" + +case platform +when "openbsd","freebsd" + set_unless[:chef][:path] = "/var/chef" + set_unless[:chef][:run_path] = "/var/run" + set_unless[:chef][:cache_path] = "/var/chef/cache" + set_unless[:chef][:serve_path] = "/var/chef" +else + set_unless[:chef][:path] = "/srv/chef" + set_unless[:chef][:run_path] = "/var/run/chef" + set_unless[:chef][:cache_path] = "#{chef[:path]}/cache" + set_unless[:chef][:serve_path] = "/srv/chef" +end + +set_unless[:chef][:server_version] = "0.7.16" +set_unless[:chef][:client_version] = "0.7.16" +set_unless[:chef][:client_interval] = "1800" +set_unless[:chef][:client_splay] = "20" +set_unless[:chef][:log_dir] = "/var/log/chef" + +case chef[:init_style] +when "runit" + set_unless[:chef][:client_log] = "STDOUT" + set_unless[:chef][:indexer_log] = "STDOUT" + set_unless[:chef][:server_log] = "STDOUT" +else + set_unless[:chef][:client_log] = "#{chef[:log_dir]}/client.log" + set_unless[:chef][:indexer_log] = "#{chef[:log_dir]}/indexer.log" + set_unless[:chef][:server_log] = "#{chef[:log_dir]}/server.log" +end + +set_unless[:chef][:server_fqdn] = domain ? "chef.#{domain}" : "chef" +set_unless[:chef][:server_token] = validation_token +set_unless[:chef][:server_ssl_req] = "/C=US/ST=Several/L=Locality/O=Example/OU=Operations/" + + "CN=#{chef[:server_fqdn]}/emailAddress=ops@#{domain}" diff --git a/chef/attributes/server_proxy.rb b/chef/attributes/server_proxy.rb new file mode 100644 index 0000000..b1657a0 --- /dev/null +++ b/chef/attributes/server_proxy.rb @@ -0,0 +1,23 @@ +# +# Author:: David Abdemoulaie +# Cookbook Name:: chef +# Attributes:: server_proxy +# +# Copyright 2009, David Abdemoulaie +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set_unless[:chef][:doc_root] = "/usr/share/chef-server-slice/public" + +set_unless[:chef][:server_proxy][:css_expire_hours] = "120" +set_unless[:chef][:server_proxy][:js_expire_hours] = "24" \ No newline at end of file diff --git a/chef/metadata.json b/chef/metadata.json new file mode 100644 index 0000000..ff57f34 --- /dev/null +++ b/chef/metadata.json @@ -0,0 +1,276 @@ +{ + "dependencies": { + "passenger_apache2": [ + + ], + "stompserver": [ + + ], + "packages": [ + + ], + "runit": [ + + ], + "couchdb": [ + + ], + "apache2": [ + + ] + }, + "maintainer_email": "cookbooks@opscode.com", + "conflicting": { + + }, + "description": "Installs and configures chef client and server", + "recipes": { + "chef::server": "Configures a chef-server as a passenger application", + "chef::server_proxy": "", + "chef::client": "Sets up a client to talk to a chef-server", + "chef": "" + }, + "providing": { + "chef::server": [ + + ], + "chef::server_proxy": [ + + ], + "chef": [ + + ], + "chef::client": [ + + ] + }, + "platforms": { + "ubuntu": [ + + ], + "rhel": [ + + ], + "centos": [ + + ], + "debian": [ + + ] + }, + "version": "0.13.0", + "name": "chef", + "replacing": { + + }, + "groupings": { + + }, + "long_description": "= IMPORTANT CHANGES:\n\nFirst be aware of important changes in this version from previous versions.\n\n== General:\n\nThe attributes have been consolidated into one file, chef.rb, rather than split amongst chef.rb, client.rb, indexer.rb and server.rb.\n\n== Client:\n\nThis cookbook no longer manages the chef package version, it manages /etc/chef/client.rb, reloads the configuration using the new ruby_block resource if the template changes.\n\nThe client service is not managed at all. It is assumed to be set up via init script or runit from package installation or bootstrap.\n\n== Server:\n\n*This cookbook no longer configures a Chef Server under Passenger by default.* \n\nThe stompserver and couchdb cookbooks are not included by default. See below under Cookbooks requirements.\n\nThe default server recipe (chef::server) sets up two Merb Mongrel workers for the webui/api (port 4000) and openid (port 4001).\n\nThe default server recipe (chef::server), creates but does not manage the chef-indexer and chef-server services, and configures both from /etc/chef/server.rb. Some package installation methods (e.g., Debian) have a separate config file for chef-indexer.\n\nThe chef::server_proxy recipe sets up an Apache proxy vhost to provide SSL in front of the chef-server running as a Merb application.\n\n= DESCRIPTION:\n\nUse this cookbook to configure a chef client to connect to your preferred chef-server, or config a chef-server.\n\n= REQUIREMENTS:\n\nChef v0.7.10, for attribute 'default' syntax.\n\n== Platform:\n\nServer is tested on Ubuntu 9.10, 9.04, 8.10 and 8.04, Debian 5.0.\n\nClient is tested on the above, plus CentOS 5.3, Fedora 10, OpenBSD 4.6, FreeBSD 7.1 and Gentoo.\n\n== Cookbooks:\n\nClient: \n\nrunit is suggested for RubyGem installations. Clients do not require any other cookbooks.\n\nServer:\n\ncouchdb and stompserver are suggested for RubyGem installations. On systems where Chef and dependencies were installed from platform packages, CouchDB and Stompserver should be installed and configured sufficiently. Localised configuration requires additional changes to the server recipe and may require changes when using the Opscode recipes.\n\nServer using server_proxy:\n\n* apache2 (opscode/cookbooks)\n\n= ATTRIBUTES:\n\n*A note about paths:* We try to stick with generally accepted FHS guidelines for path locations, but you might need to adjust these for your platform. See the filesystem hierarchy documentation for your operating system if you're not sure.\n\n=== url_type\n\nSet up the URLs the client should connect to with this. Default is 'http', which tells the client to connect to 'http://server:4000'. If you set up your chef-server to use an SSL front-end for example with chef::server_proxy, set this to 'https' and the URLs will be 'https://server/'. \n\n=== init_style\n\nSpecifies the init style to use. Default 'runit'. Other possible values 'init', 'bsd', any other string will be treated as unknown.\n\nIf your platform doesn't have a 'runit' package or if the cookbook doesn't detect it, but you stil want to use runit, set init_style to 'none' and install runit separately.\n\n=== path\n\nThis is the base location where chef will store its associated data. Default '/srv/chef' for RubyGems installed systems. The location preference varies by platform. The default is a filesystem hiearchy standard suggestion[1]. Some other locations you may consider, by platform:\n\nDebian and Red Hat based Linux distros (Ubuntu, CentOS, Fedora, etc):\n\n* /var/lib/chef\n\nAny BSD and Gentoo:\n\n* /var/chef\n\n=== run_path\n\nLocation for pidfiles on systems using init scripts. Default '/var/run/chef'.\n\nIf init_style is 'init', this is used, and should match what the init script itself uses for the PID files.\n\n=== cache_path\n\nLocation where the client will cache cookbooks and other data. Default is 'cache' underneath the bootstrap[:chef][:path] location. Linux distributions might prefer /var/cache/chef instead.\n\n=== serve_path\n\nUsed by the Chef server as the base location to \"serve\" cookbooks, roles and other assets. Default is /srv/chef.\n\n=== server_version, client_version\n\nSet the version Chef. This is now unused in the chef cookbook for any specific configuration but you can optionally override the opscode recipe with one that manages the specific version of Chef installed. Default is the latest Chef release. Informational messages may be printed using the veresion, though.\n\n=== client_interval\n\nNumber of seconds to run chef-client periodically. Default '1800' (30 minutes).\n\n=== client_splay\n\nSplay interval to randomly add to interval. Default '20'.\n\n=== log_dir\n\nDirectory where logs are stored if logs are not sent to STDOUT. Systems using runit should send logs to STDOUT as runit manages log output. Default STDOUT when init_style is 'runit', otherwise the default is '/var/log/chef'.\n\n=== client_log, indexer_log, server_log\n\nLocation of the client, indexer and server logs, respectively. Default 'STDOUT' on systems with runit, '/var/log/chef/{client,indexer,server}.log' on other systems.\n\n=== server_fqdn\n\nFully qualified domain name of the server. Default is 'chef.domain' where domain is detected by Ohai. You should configure a DNS entry for your Chef Server.\n\nOn servers, this specifies the URLs the server expects, plus it is used in the server_ssl_req as the canonical name (CN) and in server_proxy for the vhost name.\n\nOn clients, this specifies the URLs the client uses to connect to the server.\n\n=== server_token\n\nThe validation_token used to automatically authorize chef-clients. Default is a random string generated every time chef-solo runs, and can be stored as a node attribute on the server. Use chef-client -t 'validation_token' to automatically validate the client.\n\n=== server_ssl_req\n\nUsed by the server_proxy recipe, this attribute can be used to set up a self-signed SSL certificate automatically using openssl. Fields:\n\n* C: country (two letter code)\n* ST: state/province\n* L: locality or city\n* O: organization\n* OU: organizational unit\n* CN: canonical name, usually the fully qualified domain name of the server (FQDN)\n* emailAddress: contact email address\n\n= USAGE:\n\nThis cookbook is primarily designed to configure a Chef client or server with the /etc/chef/ configuration files. Server services should be restarted when the config file changes. The running client configuration will get reloaded from the template if it changes.\n\nThe primary usage would be to set up a JSON file used with chef-client -j to set the run_list and attributes. The settings could alternately be put in a role, as well. When the JSON is used, node will have the run_list and attributes saved in the Chef Server it connected to.\n\nExample JSON to set up a client:\n\n {\n \"chef\": {\n \"url_type\": \"https\",\n \"init_style\": \"init\",\n \"server_fqdn\": \"chef.example.com\"\n },\n \"recipes\": \"chef::client\"\n }\n\nThis will tell the client to use the https style URLs (see chef::client below), that we'll have init scripts set up, and to connect to the server \"chef.example.com\"\n\n=== Passenger Not Used:\n\nAs mentioned above, Passenger is no longer used as the default. Use the server_proxy recipe to create an SSL front-end.\n\n== Server Default (chef::server)\n\nBy default, the server is setup to run as a standard Merb application with the Mongrel adapter, using the package installation or the bootstrap cookbook. The chef::server recipe is used to maintain the configuration.\n\nWhen using chef::server only, clients can use the default value for url_type (http).\n\n== Server Proxy (chef::server_proxy)\n\nIf you would like to set up an SSL front end for your server, use the chef::server_proxy recipe.\n\nWhen using this recipe, clients should have the url_type attribute set to \"https\".\n\nYou will need to edit the server_ssl_request attribute so the certificate is generated correctly.\n\nThe recipe itself will set up the Apache proxy:\n\n* Add port 444 to the listen_ports (Apache's ports.conf), required for OpenID.\n* Enable Apache modules proxy proxy_http proxy_balancer ssl rewrite headers\n* Create the SSL certificate based on the server_ssl_req attribute.\n* Set up and enable virtual hosts on ports 443 and 444 in the site config \"chef_server.conf\".\n\nThe proxy will send requests from port 443 to the Mongrel running on port 4000 (webui/api) and requests on port 444 to the Mongrel on port 4001 (openid). Be sure to adjust any firewall rules or security group settings appropriately for these ports (4000, 4001, 443, 444).\n\n=== SSL Certificates\n\nThe server_proxy recipe will generate a self-signed PEM certificate on the first run. If you use opscode's chef-repo, use rake to generate your own site-specific certificate if you wish. You can also use a purchased certificate to replace the one generated through this cookbook, but it must be named by the fully qualified domain name as used in the server_fqdn attribute.\n\n== Client Default (chef::client)\n\nIf your Chef Server's fully qualified domain name is not \"chef.domain\" where domain is the node attribute detected by ohai, then you'll need to specify the server_fqdn attribute for your clients. \n\nYou may want to adjust the path attributes as described above.\n\nMake sure you specify the correct url_type for your Chef Server. This will create the URLs in the client config file as so:\n\nhttp:: http://chef.domain:4000/\n\nhttps:: https://chef.domain/\n\n(the openid_url will be :4001 and :444 respectively.)\n\n= LICENSE and AUTHOR:\n\nAuthor:: Joshua Timberman \nAuthor:: Joshua Sierles \n\nCopyright 2008-2009, Opscode, Inc\nCopyright 2009, 37signals\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "attributes": { + "chef/server_ssl_req": { + "default": "/C=US/ST=Several/L=Locality/O=Example/OU=Operations/CN=chef_server_fqdn/emailAddress=ops@domain", + "calculated": false, + "type": "string", + "choice": [ + + ], + "description": "Data to pass for creating the SSL certificate", + "display_name": "Chef Server SSL Request", + "required": "optional", + "recipes": [ + + ] + }, + "chef/server_path": { + "default": "gem_dir/gems/chef-server-chef_server_version", + "calculated": false, + "type": "string", + "choice": [ + + ], + "description": "Location of the Chef Server assets", + "display_name": "Chef Server Path", + "required": "optional", + "recipes": [ + + ] + }, + "chef/run_path": { + "default": "/var/run/chef", + "calculated": false, + "type": "string", + "choice": [ + + ], + "description": "Filesystem location for Chef 'run' files", + "display_name": "Chef Run Path", + "required": "optional", + "recipes": [ + + ] + }, + "chef/client_log": { + "default": "STDOUT", + "calculated": false, + "type": "string", + "choice": [ + + ], + "description": "Location of the chef client log", + "display_name": "Chef Client Log", + "required": "optional", + "recipes": [ + + ] + }, + "chef/path": { + "default": "/srv/chef", + "calculated": false, + "type": "string", + "choice": [ + + ], + "description": "Filesystem location for Chef files", + "display_name": "Chef Path", + "required": "optional", + "recipes": [ + + ] + }, + "chef/server_log": { + "default": "/var/log/chef/server.log", + "calculated": false, + "type": "string", + "choice": [ + + ], + "description": "Location of the Chef server log", + "display_name": "Chef Server Log", + "required": "optional", + "recipes": [ + + ] + }, + "chef/client_splay": { + "default": "20", + "calculated": false, + "type": "string", + "choice": [ + + ], + "description": "Random number of seconds to add to interval", + "display_name": "Chef Client Splay ", + "required": "optional", + "recipes": [ + + ] + }, + "chef/client_version": { + "default": "0.7.10", + "calculated": false, + "type": "string", + "choice": [ + + ], + "description": "Set the version of the client gem to install", + "display_name": "Chef Client Version", + "required": "optional", + "recipes": [ + + ] + }, + "chef/server_fqdn": { + "default": "hostname.domain", + "calculated": false, + "type": "string", + "choice": [ + + ], + "description": "FQDN of the Chef server for Apache vhost and SSL certificate and clients", + "display_name": "Chef Server Fully Qualified Domain Name", + "required": "optional", + "recipes": [ + + ] + }, + "chef/server_version": { + "default": "0.7.10", + "calculated": false, + "type": "string", + "choice": [ + + ], + "description": "Set the version of the server and server-slice gems to install", + "display_name": "Chef Server Version", + "required": "optional", + "recipes": [ + + ] + }, + "chef/indexer_log": { + "default": "/var/log/chef/indexer.log", + "calculated": false, + "type": "string", + "choice": [ + + ], + "description": "Location of the chef-indexer log", + "display_name": "Chef Indexer Log ", + "required": "optional", + "recipes": [ + + ] + }, + "chef/client_interval": { + "default": "1800", + "calculated": false, + "type": "string", + "choice": [ + + ], + "description": "Poll chef client process to run on this interval in seconds", + "display_name": "Chef Client Interval ", + "required": "optional", + "recipes": [ + + ] + }, + "chef/server_token": { + "default": "randomly generated", + "calculated": false, + "type": "string", + "choice": [ + + ], + "description": "Value of the validation_token", + "display_name": "Chef Server Validation Token", + "required": "optional", + "recipes": [ + + ] + }, + "chef/server_hostname": { + "default": "hostname", + "calculated": false, + "type": "string", + "choice": [ + + ], + "description": "Hostname for the chef server, for building FQDN", + "display_name": "Chef Server Hostname", + "required": "optional", + "recipes": [ + + ] + } + }, + "recommendations": { + + }, + "license": "Apache 2.0", + "maintainer": "Opscode, Inc.", + "suggestions": { + + } +} \ No newline at end of file diff --git a/chef/metadata.rb b/chef/metadata.rb new file mode 100644 index 0000000..b465d13 --- /dev/null +++ b/chef/metadata.rb @@ -0,0 +1,87 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures chef client and server" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.13" +recipe "chef::client", "Sets up a client to talk to a chef-server" +recipe "chef::server", "Configures a chef-server as a passenger application" + +%w{ runit packages couchdb stompserver apache2 passenger_apache2 }.each do |cb| + depends cb +end + +%w{ centos rhel ubuntu debian }.each do |os| + supports os +end + +attribute "chef/path", + :display_name => "Chef Path", + :description => "Filesystem location for Chef files", + :default => "/srv/chef" + +attribute "chef/run_path", + :display_name => "Chef Run Path", + :description => "Filesystem location for Chef 'run' files", + :default => "/var/run/chef" + +attribute "chef/client_version", + :display_name => "Chef Client Version", + :description => "Set the version of the client gem to install", + :default => "0.7.10" + +attribute "chef/client_interval", + :display_name => "Chef Client Interval ", + :description => "Poll chef client process to run on this interval in seconds", + :default => "1800" + +attribute "chef/client_splay", + :display_name => "Chef Client Splay ", + :description => "Random number of seconds to add to interval", + :default => "20" + +attribute "chef/client_log", + :display_name => "Chef Client Log", + :description => "Location of the chef client log", + :default => "STDOUT" + +attribute "chef/indexer_log", + :display_name => "Chef Indexer Log ", + :description => "Location of the chef-indexer log", + :default => "/var/log/chef/indexer.log" + +attribute "chef/server_version", + :display_name => "Chef Server Version", + :description => "Set the version of the server and server-slice gems to install", + :default => "0.7.10" + +attribute "chef/server_log", + :display_name => "Chef Server Log", + :description => "Location of the Chef server log", + :default => "/var/log/chef/server.log" + +attribute "chef/server_path", + :display_name => "Chef Server Path", + :description => "Location of the Chef Server assets", + :default => "gem_dir/gems/chef-server-chef_server_version" + +attribute "chef/server_hostname", + :display_name => "Chef Server Hostname", + :description => "Hostname for the chef server, for building FQDN", + :default => "hostname" + +attribute "chef/server_fqdn", + :display_name => "Chef Server Fully Qualified Domain Name", + :description => "FQDN of the Chef server for Apache vhost and SSL certificate and clients", + :default => "hostname.domain" + +attribute "chef/server_ssl_req", + :display_name => "Chef Server SSL Request", + :description => "Data to pass for creating the SSL certificate", + :default => "/C=US/ST=Several/L=Locality/O=Example/OU=Operations/CN=chef_server_fqdn/emailAddress=ops@domain" + +attribute "chef/server_token", + :display_name => "Chef Server Validation Token", + :description => "Value of the validation_token", + :default => "randomly generated" + diff --git a/chef/recipes/client.rb b/chef/recipes/client.rb new file mode 100644 index 0000000..999b855 --- /dev/null +++ b/chef/recipes/client.rb @@ -0,0 +1,64 @@ +# +# Author:: Joshua Timberman +# Author:: Joshua Sierles +# Cookbook Name:: chef +# Recipe:: client +# +# Copyright 2008-2009, Opscode, Inc +# Copyright 2009, 37signals +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +root_group = value_for_platform( + "openbsd" => { "default" => "wheel" }, + "freebsd" => { "default" => "wheel" }, + "default" => "root" +) + +if node[:chef][:client_log] == "STDOUT" + client_log = node[:chef][:client_log] + show_time = "false" +else + client_log = "\"#{node[:chef][:client_log]}\"" + show_time = "true" +end + +ruby_block "reload_client_config" do + block do + Chef::Config.from_file("/etc/chef/client.rb") + end + action :nothing +end + +template "/etc/chef/client.rb" do + source "client.rb.erb" + owner "root" + group root_group + mode "644" + variables( + :client_log => client_log, + :show_time => show_time + ) + notifies :create, resources(:ruby_block => "reload_client_config") +end + +execute "Register client node with Chef Server" do + command "/usr/bin/env chef-client -t \`cat /etc/chef/validation_token\`" + only_if { File.exists?("/etc/chef/validation_token") } + not_if { File.exists?("#{node[:chef][:path]}/cache/registration") } +end + +execute "Remove the validation token" do + command "rm /etc/chef/validation_token" + only_if { File.exists?("/etc/chef/validation_token") } +end diff --git a/chef/recipes/default.rb b/chef/recipes/default.rb new file mode 100644 index 0000000..e69de29 diff --git a/chef/recipes/server.rb b/chef/recipes/server.rb new file mode 100644 index 0000000..78e4ac0 --- /dev/null +++ b/chef/recipes/server.rb @@ -0,0 +1,75 @@ +# +# Author:: Joshua Timberman +# Author:: Joshua Sierles +# Cookbook Name:: chef +# Recipe:: server +# +# Copyright 2008-2009, Opscode, Inc +# Copyright 2009, 37signals +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +root_group = value_for_platform( + "openbsd" => { "default" => "wheel" }, + "freebsd" => { "default" => "wheel" }, + "default" => "root" +) + +include_recipe "chef::client" + +service "chef-indexer" do + action :nothing +end + +service "chef-server" do + action :nothing + if node[:chef][:init_style] == "runit" + restart_command "sv int chef-server" + end +end + +if node[:chef][:server_log] == "STDOUT" + server_log = node[:chef][:server_log] + show_time = "false" +else + server_log = "\"#{node[:chef][:server_log]}\"" + show_time = "true" +end + +template "/etc/chef/server.rb" do + source "server.rb.erb" + owner "root" + group root_group + mode "644" + variables( + :server_log => server_log, + :show_time => show_time + ) + notifies :restart, resources( + :service => "chef-indexer", + :service => "chef-server" + ), :delayed +end + +http_request "compact chef couchDB" do + action :post + url "#{Chef::Config[:couchdb_url]}/_compact" + only_if do + begin + open("#{Chef::Config[:couchdb_url]}/chef") + JSON::parse(open("#{Chef::Config[:couchdb_url]}/chef").read)["disk_size"] > 100_000_000 + rescue OpenURI::HTTPError + nil + end + end +end diff --git a/chef/recipes/server_proxy.rb b/chef/recipes/server_proxy.rb new file mode 100644 index 0000000..95aa687 --- /dev/null +++ b/chef/recipes/server_proxy.rb @@ -0,0 +1,61 @@ +# +# Author:: Joshua Timberman +# Cookbook Name:: chef +# Recipe:: server_proxy +# +# Copyright 2009, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +root_group = value_for_platform( + "openbsd" => { "default" => "wheel" }, + "freebsd" => { "default" => "wheel" }, + "default" => "root" +) + +node[:apache][:listen_ports] << "444" unless node[:apache][:listen_ports].include?("444") + +include_recipe "chef::server" +include_recipe "apache2" +include_recipe "apache2::mod_ssl" +include_recipe "apache2::mod_proxy" +include_recipe "apache2::mod_proxy_http" +include_recipe "apache2::mod_proxy_balancer" +include_recipe "apache2::mod_rewrite" +include_recipe "apache2::mod_headers" +include_recipe "apache2::mod_expires" +include_recipe "apache2::mod_deflate" + +directory "/etc/chef/certificates" do + owner "root" + group root_group + mode "700" +end + +bash "Create SSL Certificates" do + cwd "/etc/chef/certificates" + code <<-EOH + umask 077 + openssl genrsa 2048 > #{node[:chef][:server_fqdn]}.key + openssl req -subj "#{node[:chef][:server_ssl_req]}" -new -x509 -nodes -sha1 -days 3650 -key #{node[:chef][:server_fqdn]}.key > #{node[:chef][:server_fqdn]}.crt + cat #{node[:chef][:server_fqdn]}.key #{node[:chef][:server_fqdn]}.crt > #{node[:chef][:server_fqdn]}.pem + EOH + not_if { File.exists?("/etc/chef/certificates/#{node[:chef][:server_fqdn]}.pem") } +end + +web_app "chef_server" do + template "chef_server.conf.erb" + server_name node[:chef][:server_fqdn] + server_aliases [ node[:hostname], node[:fqdn], node[:chef][:server_fqdn] ] + log_dir node[:apache][:log_dir] +end diff --git a/chef/templates/default/chef_server.conf.erb b/chef/templates/default/chef_server.conf.erb new file mode 100644 index 0000000..8eaf49b --- /dev/null +++ b/chef/templates/default/chef_server.conf.erb @@ -0,0 +1,65 @@ + + ServerName <%= @params[:server_name] %> + ServerAlias <% @params[:server_aliases].each do |a| %><%= "#{a}" %> <% end %> + + DocumentRoot <%= @node[:chef][:doc_root] %> + + ExpiresActive On + ExpiresByType text/css "access plus <%= @node[:chef][:server_proxy][:css_expire_hours] %> hours" + ExpiresByType text/javascript "access plus <%= @node[:chef][:server_proxy][:js_expire_hours] %> hours" + ExpiresByType application/x-javascript "access plus <%= @node[:chef][:server_proxy][:js_expire_hours] %> hours" + ExpiresByType application/javascript "access plus <%= @node[:chef][:server_proxy][:js_expire_hours] %> hours" + + + BalancerMember http://127.0.0.1:4000 + Order deny,allow + Allow from all + + + LogLevel info + ErrorLog <%= @params[:log_dir] %>/<%= @params[:name] %>-error.log + CustomLog <%= @params[:log_dir] %>/<%= @params[:name] %>-access.log combined + + SSLEngine On + SSLCertificateFile /etc/chef/certificates/<%= @params[:server_name] %>.pem + SSLCertificateKeyFile /etc/chef/certificates/<%= @params[:server_name] %>.pem + + RequestHeader set X_FORWARDED_PROTO 'https' + + RewriteEngine On + RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f + RewriteRule ^/(.*)$ balancer://chef_server%{REQUEST_URI} [P,QSA,L] + + + + ServerName <%= @params[:server_name] %> + ServerAlias <% @params[:server_aliases].each do |a| %><%= "#{a}" %> <% end %> + + DocumentRoot <%= @node[:chef][:doc_root] %> + + ExpiresActive On + ExpiresByType text/css "access plus <%= @node[:chef][:server_proxy][:css_expire_hours] %> hours" + ExpiresByType text/javascript "access plus <%= @node[:chef][:server_proxy][:js_expire_hours] %> hours" + ExpiresByType application/x-javascript "access plus <%= @node[:chef][:server_proxy][:js_expire_hours] %> hours" + ExpiresByType application/javascript "access plus <%= @node[:chef][:server_proxy][:js_expire_hours] %> hours" + + + BalancerMember http://127.0.0.1:4001 + Order deny,allow + Allow from all + + + LogLevel info + ErrorLog <%= @params[:log_dir] %>/<%= @params[:name] %>-error.log + CustomLog <%= @params[:log_dir] %>/<%= @params[:name] %>-access.log combined + + SSLEngine On + SSLCertificateFile /etc/chef/certificates/<%= @params[:server_name] %>.pem + SSLCertificateKeyFile /etc/chef/certificates/<%= @params[:server_name] %>.pem + + RequestHeader set X_FORWARDED_PROTO 'https' + + RewriteEngine On + RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f + RewriteRule ^/(.*)$ balancer://chef_server_openid%{REQUEST_URI} [P,QSA,L] + diff --git a/chef/templates/default/client.rb.erb b/chef/templates/default/client.rb.erb new file mode 100644 index 0000000..a199785 --- /dev/null +++ b/chef/templates/default/client.rb.erb @@ -0,0 +1,30 @@ +# +# Chef Client Config File +# +# Dynamically generated by Chef - local modifications will be replaced +# + +log_level :info +log_location <%= @client_log %> +ssl_verify_mode :verify_none +<% if @node[:chef][:url_type] == "https" -%> +registration_url "https://<%= @node[:chef][:server_fqdn] %>" +openid_url "https://<%= @node[:chef][:server_fqdn] %>:444" +template_url "https://<%= @node[:chef][:server_fqdn] %>" +remotefile_url "https://<%= @node[:chef][:server_fqdn] %>" +search_url "https://<%= @node[:chef][:server_fqdn] %>" +role_url "https://<%= @node[:chef][:server_fqdn] %>" +<% else -%> +registration_url "http://<%= @node[:chef][:server_fqdn] %>:4000" +openid_url "http://<%= @node[:chef][:server_fqdn] %>:4001" +template_url "http://<%= @node[:chef][:server_fqdn] %>:4000" +remotefile_url "http://<%= @node[:chef][:server_fqdn] %>:4000" +search_url "http://<%= @node[:chef][:server_fqdn] %>:4000" +role_url "http://<%= @node[:chef][:server_fqdn] %>:4000" +<% end -%> + +file_cache_path "<%= @node[:chef][:cache_path] %>" + +pid_file "<%= @node[:chef][:run_path] %>/client.pid" + +Chef::Log::Formatter.show_time = <%= @show_time %> diff --git a/chef/templates/default/server.rb.erb b/chef/templates/default/server.rb.erb new file mode 100644 index 0000000..cc0ddf4 --- /dev/null +++ b/chef/templates/default/server.rb.erb @@ -0,0 +1,34 @@ +# +# Chef Server Config File +# +# Dynamically generated by Chef - local modifications will be replaced + +log_level :info +log_location <%= @server_log %> +ssl_verify_mode :verify_none +registration_url "http://<%= @node[:chef][:server_fqdn] %>:4000" +openid_url "http://<%= @node[:chef][:server_fqdn] %>:4001" +template_url "http://<%= @node[:chef][:server_fqdn] %>:4000" +remotefile_url "http://<%= @node[:chef][:server_fqdn] %>:4000" +search_url "http://<%= @node[:chef][:server_fqdn] %>:4000" +role_url "http://<%= @node[:chef][:server_fqdn] %>:4000" + +validation_token "<%= @node[:chef][:server_token] %>" + +cookbook_path [ "<%= @node[:chef][:serve_path] %>/site-cookbooks", "<%= @node[:chef][:serve_path] %>/cookbooks" ] + +file_cache_path "<%= @node[:chef][:cache_path] %>" +node_path "<%= @node[:chef][:serve_path] %>/nodes" +openid_store_path "<%= @node[:chef][:path] %>/openid/store" +openid_cstore_path "<%= @node[:chef][:path] %>/openid/cstore" +search_index_path "<%= @node[:chef][:path] %>/search_index" +role_path "<%= @node[:chef][:serve_path] %>/roles" + +umask <%= @node[:chef][:umask] %> + +# See http://wiki.opscode.com/display/chef/Securing+Chef+Server +# For more information on these settings. +#authorized_openid_providers [ "https://<%= @node[:chef][:server_fqdn]%>", "https://chef", "myopenid.com" ] +#authorized_openid_identifiers [ "" ] + +Chef::Log::Formatter.show_time = <%= @show_time %> diff --git a/collectd/attributes/collectd.rb b/collectd/attributes/collectd.rb new file mode 100755 index 0000000..d8b88d6 --- /dev/null +++ b/collectd/attributes/collectd.rb @@ -0,0 +1,22 @@ +collectd Mash.new unless attribute?("collectd") +collectd[:base_dir] = "/var/lib/collectd" unless collectd.has_key?(:base_dir) +collectd[:plugin_dir] = "/usr/lib/collectd" unless collectd.has_key?(:plugin_dir) +collectd[:types_db] = ["/usr/lib/collectd/types.db", "/etc/collectd/my_types.db"] unless collectd.has_key?(:types_db) +collectd[:interval] = 10 unless collectd.has_key?(:interval) +collectd[:read_threads] = 5 unless collectd.has_key?(:read_threads) +collectd[:server_address] = %w(192.168.1.153 192.168.1.159) +collectd[:server] = false unless collectd.has_key?(:server) + +# if !collectd.has_key?(:plugins) + collectd[:plugins] = + [ + { "name" => "syslog", "options" => [{ "LogLevel" => "Info" }]}, + { "name" => "cpu" }, + { "name" => "df", "options" => [{ "Device" => "/dev/vda1" }, { "Device" => "/dev/sda1" }]}, + { "name" => "disk", "options" => [{ "Disk" => "vda1" }, { "Disk" => "sda1" }]}, + { "name" => "interface", "options" => [{ "Interface" => "eth0" }, { "Interface" => "eth1"}]}, + { "name" => "memory" }, + { "name" => "rrdtool", "options" => [{ "DataDir" => "/var/lib/collectd/rrd" }, { "CacheFlush" => 120 }, { "WritesPerSecond" => 75 }]}, + { "name" => "swap" } + ]; +# end diff --git a/collectd/libraries/default.rb b/collectd/libraries/default.rb new file mode 100755 index 0000000..67608ad --- /dev/null +++ b/collectd/libraries/default.rb @@ -0,0 +1,6 @@ + +def format_option(option) + return option if option.instance_of?(Fixnum) || option == true || option == false + "\"#{option}\"" +end + diff --git a/collectd/metadata.json b/collectd/metadata.json new file mode 100755 index 0000000..a4802ff --- /dev/null +++ b/collectd/metadata.json @@ -0,0 +1,38 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "collectd": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures collectd", + "version": "0.1.0", + "name": "collectd", + "providing": { + "collectd": [ + + ] + } +} \ No newline at end of file diff --git a/collectd/metadata.rb b/collectd/metadata.rb new file mode 100755 index 0000000..c10d288 --- /dev/null +++ b/collectd/metadata.rb @@ -0,0 +1,4 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures collectd" +version "0.1" diff --git a/collectd/recipes/default.rb b/collectd/recipes/default.rb new file mode 100755 index 0000000..bf2af0c --- /dev/null +++ b/collectd/recipes/default.rb @@ -0,0 +1,16 @@ +package "collectd" + +service "collectd" do + supports :restart => true, :status => true +end + +%w(collectd collection thresholds).each do |file| + template "/etc/collectd/#{file}.conf" do + source "#{file}.conf.erb" + owner "root" + group "root" + mode "644" + notifies :restart, resources(:service => "collectd") + end +end + diff --git a/collectd/templates/default/collectd.conf.erb b/collectd/templates/default/collectd.conf.erb new file mode 100755 index 0000000..b786842 --- /dev/null +++ b/collectd/templates/default/collectd.conf.erb @@ -0,0 +1,40 @@ +# Config file for collectd(1). +# +# Some plugins need additional configuration and are disabled by default. +# Please read collectd.conf(5) for details. +# +# You should also read /usr/share/doc/collectd/README.Debian.plugins before +# enabling any more plugins. + +Hostname "<%= @node[:fqdn] %>" +FQDNLookup true +BaseDir "<%= @node[:collectd][:base_dir] %>" +PluginDir "<%= @node[:collectd][:plugin_dir] %>" +TypesDB "<%= @node[:collectd][:types_db].join('", "') %>" +Interval <%= @node[:collectd][:interval] %> +ReadThreads <%= @node[:collectd][:read_threads] %> + +<% @node[:collectd][:plugins].each do |plugin| %> +LoadPlugin "<%= plugin[:name] %>" +<% end %> + +<% @node[:collectd][:plugins].each do |plugin| %> +<% if plugin[:options] %> +> + <% plugin[:options].each do |opt| %> + <%= opt.keys.first %> <%= format_option(opt[opt.keys.first]) %> + <% end %> + + +<% end %> +<% end %> + +LoadPlugin "network" + + <% @node[:collectd][:server_address].each do |address| -%> + Server "<%= address %>" + <% end -%> + <% if @node[:collectd][:listen_address] %>Listen "<%= @node[:collectd][:listen_address] %>"<% end -%> + + +Include "/etc/collectd/thresholds.conf" diff --git a/collectd/templates/default/collection.conf.erb b/collectd/templates/default/collection.conf.erb new file mode 100755 index 0000000..958c7a6 --- /dev/null +++ b/collectd/templates/default/collection.conf.erb @@ -0,0 +1,3 @@ +datadir: "/var/lib/collectd/rrd/" +libdir: "/usr/lib/collectd/" + diff --git a/collectd/templates/default/thresholds.conf.erb b/collectd/templates/default/thresholds.conf.erb new file mode 100755 index 0000000..bff223a --- /dev/null +++ b/collectd/templates/default/thresholds.conf.erb @@ -0,0 +1,38 @@ +# Threshold configuration for collectd(1). +# +# See the section "THRESHOLD CONFIGURATION" in collectd.conf(5) for details. + +# +# +# WarningMin 0.00 +# WarningMax 1000.00 +# FailureMin 0 +# FailureMax 1200.00 +# Invert false +# Persist false +# Instance "some_instance" +# +# +# +# Instance "eth0" +# +# DataSource "rx" +# FailureMax 10000000 +# +# +# +# +# +# Instance "idle" +# FailureMin 10 +# +# +# +# +# Instance "cached" +# WarningMin 100000000 +# +# +# +# + diff --git a/couchdb/metadata.json b/couchdb/metadata.json new file mode 100644 index 0000000..35ee567 --- /dev/null +++ b/couchdb/metadata.json @@ -0,0 +1,58 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs CouchDB package and starts service", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "couchdb": "" + }, + "suggestions": { + + }, + "platforms": { + "rhel": [ + + ], + "freebsd": [ + + ], + "ubuntu": [ + ">= 8.10" + ], + "openbsd": [ + + ], + "fedora": [ + + ], + "centos": [ + + ], + "debian": [ + ">= 5.0" + ] + }, + "version": "0.8.0", + "name": "couchdb", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "couchdb": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "Installs the CouchDB package if it is available from an package repository on\nthe node. If the package repository is not available, CouchDB needs to be \ninstalled via some other method, either a backported package, or compiled \ndirectly from source. CouchDB is available on Red Hat-based systems through\nthe EPEL Yum Repository.\n", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/couchdb/metadata.rb b/couchdb/metadata.rb new file mode 100644 index 0000000..18e2046 --- /dev/null +++ b/couchdb/metadata.rb @@ -0,0 +1,21 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs CouchDB package and starts service" +long_description <<-EOH +Installs the CouchDB package if it is available from an package repository on +the node. If the package repository is not available, CouchDB needs to be +installed via some other method, either a backported package, or compiled +directly from source. CouchDB is available on Red Hat-based systems through +the EPEL Yum Repository. +EOH +version "0.8" + +supports "ubuntu", ">= 8.10" # for package in APT +supports "debian", ">= 5.0" # for package in APT +supports "openbsd" +supports "freebsd" + +%w{ rhel centos fedora }.each do |os| + supports os # requires EPEL Yum Repository +end diff --git a/couchdb/recipes/default.rb b/couchdb/recipes/default.rb new file mode 100644 index 0000000..5e6590a --- /dev/null +++ b/couchdb/recipes/default.rb @@ -0,0 +1,47 @@ +# +# Author:: Joshua Timberman +# Cookbook Name:: couchdb +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package "couchdb" do + package_name value_for_platform( + "openbsd" => { "default" => "apache-couchdb" }, + "gentoo" => { "default" => "dev-db/couchdb" }, + "default" => "couchdb" + ) +end + +directory "/var/lib/couchdb" do + owner "couchdb" + group "couchdb" + recursive true + path value_for_platform( + "openbsd" => { "default" => "/var/couchdb" }, + "freebsd" => { "default" => "/var/couchdb" }, + "gentoo" => { "default" => "/var/couchdb" }, + "default" => "/var/lib/couchdb" + ) +end + +service "couchdb" do + if platform?("centos","redhat","fedora") + start_command "/sbin/service couchdb start &> /dev/null" + stop_command "/sbin/service couchdb stop &> /dev/null" + end + supports [ :restart, :status ] + action [ :enable, :start ] +end diff --git a/couchdb/templates/default/port_couchdb.erb b/couchdb/templates/default/port_couchdb.erb new file mode 100644 index 0000000..a11f8aa --- /dev/null +++ b/couchdb/templates/default/port_couchdb.erb @@ -0,0 +1,2 @@ +# CouchDB +-A FWR -p tcp -m tcp --dport 5984 -j ACCEPT \ No newline at end of file diff --git a/cron/attributes/cron.rb b/cron/attributes/cron.rb new file mode 100755 index 0000000..5a18f92 --- /dev/null +++ b/cron/attributes/cron.rb @@ -0,0 +1 @@ +cron Mash.new unless attribute?(:cron) \ No newline at end of file diff --git a/cron/metadata.json b/cron/metadata.json new file mode 100755 index 0000000..cc9bc03 --- /dev/null +++ b/cron/metadata.json @@ -0,0 +1,38 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "cron": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures cron", + "version": "0.1.0", + "name": "cron", + "providing": { + "cron": [ + + ] + } +} \ No newline at end of file diff --git a/cron/metadata.rb b/cron/metadata.rb new file mode 100755 index 0000000..093858f --- /dev/null +++ b/cron/metadata.rb @@ -0,0 +1,4 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures cron" +version "0.1" diff --git a/cron/recipes/default.rb b/cron/recipes/default.rb new file mode 100755 index 0000000..fe07073 --- /dev/null +++ b/cron/recipes/default.rb @@ -0,0 +1,9 @@ +if node[:cron][:jobs] + node[:cron][:jobs].each do |name, config| + cron name do + config.each do |k,v| + send(k.to_sym, v) + end + end + end +end \ No newline at end of file diff --git a/ddclient/metadata.json b/ddclient/metadata.json new file mode 100755 index 0000000..e7f6851 --- /dev/null +++ b/ddclient/metadata.json @@ -0,0 +1,38 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "ddclient": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures ddclient", + "version": "0.1.0", + "name": "ddclient", + "providing": { + "ddclient": [ + + ] + } +} \ No newline at end of file diff --git a/ddclient/metadata.rb b/ddclient/metadata.rb new file mode 100755 index 0000000..b75b963 --- /dev/null +++ b/ddclient/metadata.rb @@ -0,0 +1,4 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures ddclient" +version "0.1" diff --git a/ddclient/recipes/default.rb b/ddclient/recipes/default.rb new file mode 100755 index 0000000..86f56dd --- /dev/null +++ b/ddclient/recipes/default.rb @@ -0,0 +1,24 @@ +package "ddclient" + +service "ddclient" do + supports :restart => true + action :enable +end + +template "/etc/default/ddclient" do + source "ddclient.default.erb" + mode 0644 + owner "root" + notifies :restart, resources(:service => "ddclient") +end + +template "/etc/ddclient.conf" do + source "ddclient.conf.erb" + mode 0600 + owner "root" + notifies :restart, resources(:service => "ddclient") +end + +service "ddclient" do + action :start +end \ No newline at end of file diff --git a/ddclient/templates/default/ddclient.conf.erb b/ddclient/templates/default/ddclient.conf.erb new file mode 100755 index 0000000..7118aef --- /dev/null +++ b/ddclient/templates/default/ddclient.conf.erb @@ -0,0 +1,7 @@ +pid=/var/run/ddclient.pid +protocol=dyndns2 +server=update.dynect.net +use=web +login=<%= @node[:ddclient][:dyndns_login] %> +password=<%= @node[:ddclient][:dyndns_password] %> +<%= "#{@node[:public_fqdn]|| @node[:fqdn]}" %> \ No newline at end of file diff --git a/ddclient/templates/default/ddclient.default.erb b/ddclient/templates/default/ddclient.default.erb new file mode 100755 index 0000000..fa96986 --- /dev/null +++ b/ddclient/templates/default/ddclient.default.erb @@ -0,0 +1,2 @@ +run_daemon="true" +daemon_interval="60" diff --git a/django/README.rdoc b/django/README.rdoc new file mode 100644 index 0000000..9c3a9ca --- /dev/null +++ b/django/README.rdoc @@ -0,0 +1,42 @@ += DESCRIPTION: + +Installs Python Django package and sets up Apache2 to serve a django application. + += REQUIREMENTS: + +Opscode cookbooks, http://github.com/opscode/cookbooks/tree/master: + +* python +* apache2 + += ATTRIBUTES: + +None. + += USAGE: + +Create the django application using the Apache2 cookbook's web_app define. Normally this would be done in a site-cookbook. + + web_app "mysite" do + docroot "/srv/mysite" + template "mysite.conf.erb" + end + +Create the template, 'mysite.conf.erb' within the site-cookbook. Make sure the django settings are correct. The web_app define copies the template over and enables it as an apache2 site. + += LICENSE & AUTHOR: + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/django/metadata.json b/django/metadata.json new file mode 100644 index 0000000..f3acdc3 --- /dev/null +++ b/django/metadata.json @@ -0,0 +1,48 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs DJango", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "django": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "django", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "django": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nInstalls Python Django package and sets up Apache2 to serve a django application.\n\n= REQUIREMENTS:\n\nOpscode cookbooks, http:\/\/github.com\/opscode\/cookbooks\/tree\/master:\n\n* python\n* apache2\n\n= ATTRIBUTES:\n\nNone.\n\n= USAGE:\n\nCreate the django application using the Apache2 cookbook's web_app define. Normally this would be done in a site-cookbook.\n\n web_app \"mysite\" do\n docroot \"\/srv\/mysite\"\n template \"mysite.conf.erb\"\n end\n\nCreate the template, 'mysite.conf.erb' within the site-cookbook. Make sure the django settings are correct. The web_app define copies the template over and enables it as an apache2 site.\n\n= LICENSE & AUTHOR:\n\nAuthor:: Joshua Timberman ()\nCopyright:: 2009, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "replacing": { + + }, + "dependencies": { + "python": [ + + ], + "apache2": [ + + ] + } +} \ No newline at end of file diff --git a/django/metadata.rb b/django/metadata.rb new file mode 100644 index 0000000..9cbbb6f --- /dev/null +++ b/django/metadata.rb @@ -0,0 +1,14 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs DJango" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.7" + +%w{ ubuntu debian }.each do |os| + supports os +end + +%w{ apache2 python}.each do |cb| + depends cb +end diff --git a/django/recipes/default.rb b/django/recipes/default.rb new file mode 100644 index 0000000..6a204f3 --- /dev/null +++ b/django/recipes/default.rb @@ -0,0 +1,29 @@ +# +# Cookbook Name:: django +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +include_recipe "apache2" +include_recipe "apache2::mod_python" +include_recipe "python" + +package "apache2-mpm-prefork" do + action :install +end + +package "python-django" do + action :install +end diff --git a/djbdns/attributes/djbdns.rb b/djbdns/attributes/djbdns.rb new file mode 100644 index 0000000..68f2fb7 --- /dev/null +++ b/djbdns/attributes/djbdns.rb @@ -0,0 +1,45 @@ +# +# Cookbook Name:: djbdns +# Recipe:: default +# Author:: Joshua Timberman () +# Author:: Joshua Sierles () +# +# Copyright 2009, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set_unless[:djbdns][:tinydns_ipaddress] = "127.0.0.1" +set_unless[:djbdns][:tinydns_internal_ipaddress] = "127.0.0.1" +set_unless[:djbdns][:public_dnscache_ipaddress] = ipaddress +set_unless[:djbdns][:axfrdns_ipaddress] = "127.0.0.1" + +set_unless[:djbdns][:public_dnscache_allowed_networks] = [ipaddress.split(".")[0,2].join(".")] +set_unless[:djbdns][:tinydns_internal_resolved_domain] = domain + +case platform +when "ubuntu" + if platform_version >= "8.10" + set[:djbdns][:bin_dir] = "/usr/bin" + else + set[:djbdns][:bin_dir] = "/usr/local/bin" + end +when "debian" + if platform_version >= "5.0" + set[:djbdns][:bin_dir] = "/usr/bin" + else + set[:djbdns][:bin_dir] = "/usr/local/bin" + end +else + set[:djbdns][:bin_dir] = "/usr/local/bin" +end diff --git a/djbdns/metadata.json b/djbdns/metadata.json new file mode 100644 index 0000000..2bc50cc --- /dev/null +++ b/djbdns/metadata.json @@ -0,0 +1,149 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs djbdns and configures DNS services", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "djbdns::internal_server": "Sets up internal TinyDNS", + "djbdns": "Installs djbdns from package or source and creates users", + "djbdns": "", + "djbdns::server": "Sets up external TinyDNS", + "djbdns::axfr": "Sets up djbdns AXFR service", + "djbdns::cache": "Sets up public dnscache service" + }, + "suggestions": { + + }, + "platforms": { + "rhel": [ + + ], + "ubuntu": [ + + ], + "centos": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "djbdns", + "conflicting": { + + }, + "attributes": { + "djbdns\/axfrdns_ipaddress": { + "default": "127.0.0.1", + "type": "string", + "multiple_values": false, + "description": "Specify the IP address for AXFR service", + "display_name": "DJB DNS AXFR IP Address", + "recipes": [ + + ], + "required": false + }, + "djbdns\/public_dnscache_allowed_networks": { + "default": [ + "ipaddress.split('.')[0,2].join('.')" + ], + "type": "array", + "multiple_values": false, + "description": "Networks allowed to query the public dnscache", + "display_name": "DJB DNS Public DNS Cache Allowed Networks", + "recipes": [ + + ], + "required": false + }, + "djbdns\/tinydns_internal_ipaddress": { + "default": "127.0.0.1", + "type": "string", + "multiple_values": false, + "description": "Specify the IP address for internal TinyDNS", + "display_name": "DJB DNS TinyDNS Internal IP Address", + "recipes": [ + + ], + "required": false + }, + "djbdns\/tinydns_internal_resolved_domain": { + "default": "domain", + "type": "string", + "multiple_values": false, + "description": "Internal domain TinyDNS is resolver", + "display_name": "DJB DNS TinyDNS Internal Resolved Domain", + "recipes": [ + + ], + "required": false + }, + "djbdns\/public_dnscache_ipaddress": { + "default": "ipaddress", + "type": "string", + "multiple_values": false, + "description": "Specify the IP address for the public dnscache", + "display_name": "DJB DNS Public DNS Cache IP Address", + "recipes": [ + + ], + "required": false + }, + "djbdns\/bin_dir": { + "default": "\/usr\/local\/bin", + "type": "string", + "multiple_values": false, + "description": "Location of the djbdns binaries", + "display_name": "DJB DNS Binaries Directory", + "recipes": [ + + ], + "required": false + }, + "djbdns\/tinydns_ipaddress": { + "default": "127.0.0.1", + "type": "string", + "multiple_values": false, + "description": "Specify the IP address for TinyDNS", + "display_name": "DJB DNS TinyDNS IP Address", + "recipes": [ + + ], + "required": false + } + }, + "providing": { + "djbdns::internal_server": [ + + ], + "djbdns": [ + + ], + "djbdns::server": [ + + ], + "djbdns::axfr": [ + + ], + "djbdns::cache": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + "runit": [ + + ], + "build-essential": [ + + ] + } +} \ No newline at end of file diff --git a/djbdns/metadata.rb b/djbdns/metadata.rb new file mode 100644 index 0000000..06a3f25 --- /dev/null +++ b/djbdns/metadata.rb @@ -0,0 +1,55 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs djbdns and configures DNS services" +version "0.7" +recipe "djbdns", "Installs djbdns from package or source and creates users" +recipe "djbdns::axfr", "Sets up djbdns AXFR service" +recipe "djbdns::cache", "Sets up public dnscache service" +recipe "djbdns::internal_server", "Sets up internal TinyDNS" +recipe "djbdns::server", "Sets up external TinyDNS" + +%w{ build-essential runit }.each do |cb| + depends cb +end + +%w{ ubuntu debian centos rhel }.each do |os| + supports os +end + +attribute "djbdns/tinydns_ipaddress", + :display_name => "DJB DNS TinyDNS IP Address", + :description => "Specify the IP address for TinyDNS", + :default => "127.0.0.1" + +attribute "djbdns/tinydns_internal_ipaddress", + :display_name => "DJB DNS TinyDNS Internal IP Address", + :description => "Specify the IP address for internal TinyDNS", + :default => "127.0.0.1" + +attribute "djbdns/axfrdns_ipaddress", + :display_name => "DJB DNS AXFR IP Address", + :description => "Specify the IP address for AXFR service", + :default => "127.0.0.1" + +attribute "djbdns/public_dnscache_ipaddress", + :display_name => "DJB DNS Public DNS Cache IP Address", + :description => "Specify the IP address for the public dnscache", + :default => "ipaddress" + +attribute "djbdns/public_dnscache_allowed_networks", + :display_name => "DJB DNS Public DNS Cache Allowed Networks", + :description => "Networks allowed to query the public dnscache", + :type => "array", + :default => ["ipaddress.split('.')[0,2].join('.')"] + +attribute "djbdns/tinydns_internal_resolved_domain", + :display_name => "DJB DNS TinyDNS Internal Resolved Domain", + :description => "Internal domain TinyDNS is resolver", + :default => "domain" + +attribute "djbdns/bin_dir", + :display_name => "DJB DNS Binaries Directory", + :description => "Location of the djbdns binaries", + :default => "/usr/local/bin" + diff --git a/djbdns/recipes/axfr.rb b/djbdns/recipes/axfr.rb new file mode 100644 index 0000000..eca2a4d --- /dev/null +++ b/djbdns/recipes/axfr.rb @@ -0,0 +1,40 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: djbdns +# Recipe:: axfr +# +# Copyright 2009, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +include_recipe "djbdns" + +user "axfrdns" do + uid 9996 + case node[:platform] + when "ubuntu","debian" + gid "nogroup" + when "redhat", "centos" + gid "nobody" + else + gid "nobody" + end + shell "/bin/false" + home "/home/axfrdns" +end + +execute "#{node[:djbdns][:bin_dir]}/axfrdns-conf axfrdns dnslog #{node[:runit][:sv_dir]}/axfrdns #{node[:runit][:sv_dir]}/tinydns #{node[:djbdns][:axfrdns_ipaddress]}" do + only_if "/usr/bin/test ! -d #{node[:runit][:sv_dir]}/axfrdns" +end + +runit_service "axfrdns" diff --git a/djbdns/recipes/cache.rb b/djbdns/recipes/cache.rb new file mode 100644 index 0000000..1034e4a --- /dev/null +++ b/djbdns/recipes/cache.rb @@ -0,0 +1,43 @@ +# +# Author:: Joshua Timberman () +# Author:: Joshua Sierles () +# Cookbook Name:: djbdns +# Recipe:: cache +# +# Copyright 2009, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +include_recipe "djbdns" + +execute "public_cache_update" do + cwd "#{node[:runit][:sv_dir]}/public-dnscache" + command "#{node[:djbdns][:bin_dir]}/dnsip `#{node[:djbdns][:bin_dir]}/dnsqr ns . | awk '/answer:/ { print \$5 ; }' | sort` > root/servers/@" + action :nothing +end + +execute "#{node[:djbdns][:bin_dir]}/dnscache-conf dnscache dnslog #{node[:runit][:sv_dir]}/public-dnscache #{node[:djbdns][:public_dnscache_ipaddress]}" do + only_if "/usr/bin/test ! -d #{node[:runit][:sv_dir]}/public-dnscache" + notifies :run, resources("execute[public_cache_update]") +end + +runit_service "public-dnscache" + +file "#{node[:runit][:sv_dir]}/public-dnscache/root/ip/#{node[:djbdns][:public_dnscache_allowed_networks]}" do + mode 0644 +end + +template "#{node[:runit][:sv_dir]}/public-dnscache/root/servers/#{node[:djbdns][:tinydns_internal_resolved_domain]}" do + source "dnscache-servers.erb" + mode 0644 +end diff --git a/djbdns/recipes/default.rb b/djbdns/recipes/default.rb new file mode 100644 index 0000000..f5238d5 --- /dev/null +++ b/djbdns/recipes/default.rb @@ -0,0 +1,101 @@ +# +# Cookbook Name:: djbdns +# Recipe:: default +# Author:: Joshua Timberman () +# +# Copyright 2009, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +include_recipe "build-essential" +include_recipe "runit" + +installation_method = value_for_platform( + "debian" => { "4.0" => "source", "default" => "package" }, + "ubuntu" => { + "6.06" => "source", + "6.10" => "source", + "7.04" => "source", + "7.10" => "source", + "8.04" => "source", + "default" => "package" + }, + "default" => { "default" => "source" } +) + +case installation_method +when "package" + package "djbdns" do + action :install + end +when "source" + bash "install_djbdns" do + user "root" + cwd "/tmp" + code <<-EOH + (cd /tmp; wget http://cr.yp.to/ucspi-tcp/ucspi-tcp-0.88.tar.gz) + (cd /tmp; tar zxvf ucspi-tcp-0.88.tar.gz) + (cd /tmp/ucspi-tcp-0.88; perl -pi -e 's/extern int errno;/\#include /' error.h) + (cd /tmp/ucspi-tcp-0.88; make setup check) + (cd /tmp; wget http://cr.yp.to/djbdns/djbdns-1.05.tar.gz) + (cd /tmp; tar xzvf djbdns-1.05.tar.gz) + (cd /tmp/djbdns-1.05; perl -pi -e 's/extern int errno;/\#include /' error.h) + (cd /tmp/djbdns-1.05; make setup check) + EOH + only_if "/usr/bin/test ! -f #{node[:djbdns][:bin_dir]}/tinydns" + end +else + Chef::Log.info("Could not find an installation method for platform #{node[:platform]}, version #{node[:platform_version]}") +end + +user "dnscache" do + uid 9997 + case node[:platform] + when "ubuntu","debian" + gid "nogroup" + when "redhat", "centos" + gid "nobody" + else + gid "nobody" + end + shell "/bin/false" + home "/home/dnscache" +end + +user "dnslog" do + uid 9998 + case node[:platform] + when "ubuntu","debian" + gid "nogroup" + when "redhat", "centos" + gid "nobody" + else + gid "nobody" + end + shell "/bin/false" + home "/home/dnslog" +end + +user "tinydns" do + uid 9999 + case node[:platform] + when "ubuntu","debian" + gid "nogroup" + when "redhat", "centos" + gid "nobody" + else + gid "nobody" + end + shell "/bin/false" + home "/home/tinydns" +end diff --git a/djbdns/recipes/internal_server.rb b/djbdns/recipes/internal_server.rb new file mode 100644 index 0000000..ce4999a --- /dev/null +++ b/djbdns/recipes/internal_server.rb @@ -0,0 +1,38 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: djbdns +# Recipe:: internal_server +# +# Copyright 2009, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +include_recipe "djbdns" + +execute "#{node[:djbdns][:bin_dir]}/tinydns-conf tinydns dnslog #{node[:runit][:sv_dir]}/tinydns-internal #{node[:djbdns][:tinydns_ipaddress]}" do + only_if "/usr/bin/test ! -d #{node[:runit][:sv_dir]}/tinydns-internal" +end + +execute "build-tinydns-internal-data" do + cwd "#{node[:runit][:sv_dir]}/tinydns-internal/root" + command "make" + action :nothing +end + +template "#{node[:runit][:sv_dir]}/tinydns-internal/root/data" do + source "tinydns-internal-data.erb" + mode 0644 + notifies :run, resources("execute[build-tinydns-internal-data]") +end + +runit_service "tinydns-internal" diff --git a/djbdns/recipes/server.rb b/djbdns/recipes/server.rb new file mode 100644 index 0000000..a9a879d --- /dev/null +++ b/djbdns/recipes/server.rb @@ -0,0 +1,38 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: djbdns +# Recipe:: server +# +# Copyright 2009, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +include_recipe "djbdns" + +execute "#{node[:djbdns][:bin_dir]}/tinydns-conf tinydns dnslog #{node[:runit][:sv_dir]}/tinydns #{node[:djbdns][:tinydns_ipaddress]}" do + only_if "/usr/bin/test ! -d #{node[:runit][:sv_dir]}/tinydns" +end + +execute "build-tinydns-data" do + cwd "#{node[:runit][:sv_dir]}/tinydns/root" + command "make" + action :nothing +end + +template "#{node[:runit][:sv_dir]}/tinydns/root/data" do + source "tinydns-data.erb" + mode 0644 + notifies :run, resources("execute[build-tinydns-data]") +end + +runit_service "tinydns" diff --git a/djbdns/templates/default/dnscache-servers.erb b/djbdns/templates/default/dnscache-servers.erb new file mode 100644 index 0000000..e56ea71 --- /dev/null +++ b/djbdns/templates/default/dnscache-servers.erb @@ -0,0 +1 @@ +127.0.0.1 \ No newline at end of file diff --git a/djbdns/templates/default/sv-axfrdns-log-run.erb b/djbdns/templates/default/sv-axfrdns-log-run.erb new file mode 100644 index 0000000..2936bca --- /dev/null +++ b/djbdns/templates/default/sv-axfrdns-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec chpst -U dnslog svlogd -tt ./main diff --git a/djbdns/templates/default/sv-axfrdns-run.erb b/djbdns/templates/default/sv-axfrdns-run.erb new file mode 100644 index 0000000..338cafd --- /dev/null +++ b/djbdns/templates/default/sv-axfrdns-run.erb @@ -0,0 +1,4 @@ +#!/bin/sh +exec 2>&1 +exec chpst -e ./env sh -c ' + exec chpst -U axfrdns -d300000 tcpserver -vDRHl0 -x tcp.cdb -- "$IP" 53 <%= @node[:djbdns][:bin_dir] %>/axfrdns' diff --git a/djbdns/templates/default/sv-public-dnscache-log-run.erb b/djbdns/templates/default/sv-public-dnscache-log-run.erb new file mode 100644 index 0000000..2936bca --- /dev/null +++ b/djbdns/templates/default/sv-public-dnscache-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec chpst -U dnslog svlogd -tt ./main diff --git a/djbdns/templates/default/sv-public-dnscache-run.erb b/djbdns/templates/default/sv-public-dnscache-run.erb new file mode 100644 index 0000000..32b6248 --- /dev/null +++ b/djbdns/templates/default/sv-public-dnscache-run.erb @@ -0,0 +1,6 @@ +#!/bin/sh +exec 2>&1 +exec /dnscache +' diff --git a/djbdns/templates/default/sv-tinydns-internal-log-run.erb b/djbdns/templates/default/sv-tinydns-internal-log-run.erb new file mode 100644 index 0000000..2936bca --- /dev/null +++ b/djbdns/templates/default/sv-tinydns-internal-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec chpst -U dnslog svlogd -tt ./main diff --git a/djbdns/templates/default/sv-tinydns-internal-run.erb b/djbdns/templates/default/sv-tinydns-internal-run.erb new file mode 100644 index 0000000..9a4b80c --- /dev/null +++ b/djbdns/templates/default/sv-tinydns-internal-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec chpst -U tinydns -e ./env -d300000 <%= @node[:djbdns][:bin_dir] %>/tinydns diff --git a/djbdns/templates/default/sv-tinydns-log-run.erb b/djbdns/templates/default/sv-tinydns-log-run.erb new file mode 100644 index 0000000..2936bca --- /dev/null +++ b/djbdns/templates/default/sv-tinydns-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec chpst -U dnslog svlogd -tt ./main diff --git a/djbdns/templates/default/sv-tinydns-run.erb b/djbdns/templates/default/sv-tinydns-run.erb new file mode 100644 index 0000000..9a4b80c --- /dev/null +++ b/djbdns/templates/default/sv-tinydns-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec chpst -U tinydns -e ./env -d300000 <%= @node[:djbdns][:bin_dir] %>/tinydns diff --git a/djbdns/templates/default/tinydns-data.erb b/djbdns/templates/default/tinydns-data.erb new file mode 100644 index 0000000..2a7a5df --- /dev/null +++ b/djbdns/templates/default/tinydns-data.erb @@ -0,0 +1,4 @@ +# Auto-generated by chef for <%= @node[:fqdn] %> +# + +.<%= @node[:domain] %>:<%= @node[:djbdns][:tinydns_ipaddress] %>:a:259200 \ No newline at end of file diff --git a/djbdns/templates/default/tinydns-internal-data.erb b/djbdns/templates/default/tinydns-internal-data.erb new file mode 100644 index 0000000..314e5d9 --- /dev/null +++ b/djbdns/templates/default/tinydns-internal-data.erb @@ -0,0 +1,5 @@ +# Auto-generated by chef for <%= @node[:fqdn] %> +# +# This is the internal zone file. + +.<%= @node[:domain] %>:<%= @node[:djbdns][:tinydns_internal_ipaddress] %>:a:259200 \ No newline at end of file diff --git a/dns/metadata.json b/dns/metadata.json new file mode 100755 index 0000000..ef7f435 --- /dev/null +++ b/dns/metadata.json @@ -0,0 +1,42 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "dns::client": "", + "dns": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures dns", + "version": "0.1.0", + "name": "dns", + "providing": { + "dns::client": [ + + ], + "dns": [ + + ] + } +} \ No newline at end of file diff --git a/dns/metadata.rb b/dns/metadata.rb new file mode 100755 index 0000000..b44c1ee --- /dev/null +++ b/dns/metadata.rb @@ -0,0 +1,4 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures dns" +version "0.1" diff --git a/dns/recipes/client.rb b/dns/recipes/client.rb new file mode 100755 index 0000000..28fbd48 --- /dev/null +++ b/dns/recipes/client.rb @@ -0,0 +1,7 @@ +if node[:nameservers] + template "/etc/resolv.conf" do + source "resolv.conf.erb" + variables ({:domain => node[:domain], :nameservers => node[:nameservers], :search => node[:domain]}) + not_if { node[:nameservers].include?(node[:ipaddress]) || node.role?("development") } + end +end \ No newline at end of file diff --git a/dns/recipes/default.rb b/dns/recipes/default.rb new file mode 100755 index 0000000..e69de29 diff --git a/dns/templates/default/resolv.conf.erb b/dns/templates/default/resolv.conf.erb new file mode 100755 index 0000000..fb337d0 --- /dev/null +++ b/dns/templates/default/resolv.conf.erb @@ -0,0 +1,4 @@ +domain <%= @domain %> +<% @nameservers.each do |ns| %> +nameserver <%= ns %> +<% end %> \ No newline at end of file diff --git a/drbd/metadata.json b/drbd/metadata.json new file mode 100644 index 0000000..50ee80c --- /dev/null +++ b/drbd/metadata.json @@ -0,0 +1,45 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs but does not configure drbd", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "drbd": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "drbd", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "drbd": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + "lvm": [ + + ] + } +} \ No newline at end of file diff --git a/drbd/metadata.rb b/drbd/metadata.rb new file mode 100644 index 0000000..07f15d3 --- /dev/null +++ b/drbd/metadata.rb @@ -0,0 +1,10 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs but does not configure drbd" +version "0.7" +depends "lvm" + +%w{ ubuntu debian}.each do |os| + supports os +end diff --git a/drbd/recipes/default.rb b/drbd/recipes/default.rb new file mode 100644 index 0000000..c6aabeb --- /dev/null +++ b/drbd/recipes/default.rb @@ -0,0 +1,35 @@ +# +# Cookbook Name:: drbd +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# = Requirements +# Templates:: +# * /etc/drbd.conf + +include_recipe "lvm" + +package "drbd8-utils" do + action :install +end + +service "drbd" do + supports( + :restart => true, + :status => true + ) + action :enable +end diff --git a/dynomite/README.rdoc b/dynomite/README.rdoc new file mode 100644 index 0000000..4e1b6aa --- /dev/null +++ b/dynomite/README.rdoc @@ -0,0 +1,52 @@ += DESCRIPTION: + +Installs and configures dynomite. + += REQUIREMENTS: + +== Platform: + +Ubuntu, mainly because of dependencies on Ubuntu specific installation methods in dependencies (thrift and boost). + +== Cookbooks: + +Opscode/cookbooks: + +* thrift +** boost +* ruby +* git +* erlang +* runit + += ATTRIBUTES: + +* dynomite[:master] - whether this node is a master cluster node. +* dynomite[:cluster_name] - name of the dynomite cluster. +* dynomite[:data_dir] - location of the data. +* dynomite[:num_nodes] - number of nodes in the cluster. +* dynomite[:node_name] - name of this node. +* dynomite[:ascii_port] - port for ASCII protocol. +* dynomite[:thrift_port] - port for Thrift protocol. +* dynomite[:web_port] - port for web (HTTP). + += USAGE: + +Nothing fancy. Include the recipe, or add to a run_list like any other recipe. This cookbook relies on the ruby-dynomite client, which manages dynomite processes. The cookbook will set up dynomite as a runit service. + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/dynomite/attributes/dynomite.rb b/dynomite/attributes/dynomite.rb new file mode 100644 index 0000000..a72682b --- /dev/null +++ b/dynomite/attributes/dynomite.rb @@ -0,0 +1,31 @@ +# +# Cookbook Name:: dynomite +# attributes:: dynomite +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set_unless[:dynomite][:master] = false +set_unless[:dynomite][:cluster_name] = "dynomite" +set_unless[:dynomite][:data_dir] = "/var/db/dynomite" +set_unless[:dynomite][:num_nodes] = 1 +set_unless[:dynomite][:node_name] = hostname +set_unless[:dynomite][:ascii_port] = 11221 +set_unless[:dynomite][:thrift_port] = 11222 +set_unless[:dynomite][:web_port] = 1180 + +master_node = search(:node, "dynomite_master:true", "fqdn").first + +set_unless[:dynomite][:join_node] = master_node diff --git a/dynomite/metadata.json b/dynomite/metadata.json new file mode 100644 index 0000000..3a8fd34 --- /dev/null +++ b/dynomite/metadata.json @@ -0,0 +1,51 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs\/Configures dynomite", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "dynomite": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ] + }, + "version": "0.1.0", + "name": "dynomite", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "dynomite": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nInstalls and configures dynomite.\n\n= REQUIREMENTS:\n\n== Platform:\n\nUbuntu, mainly because of dependencies on Ubuntu specific installation methods in dependencies (thrift and boost).\n\n== Cookbooks:\n\nOpscode\/cookbooks:\n\n* thrift\n** boost\n* ruby\n* git\n* erlang\n* runit\n\n= ATTRIBUTES: \n\n* dynomite[:master] - whether this node is a master cluster node.\n* dynomite[:cluster_name] - name of the dynomite cluster.\n* dynomite[:data_dir] - location of the data.\n* dynomite[:num_nodes] - number of nodes in the cluster.\n* dynomite[:node_name] - name of this node.\n* dynomite[:ascii_port] - port for ASCII protocol.\n* dynomite[:thrift_port] - port for Thrift protocol.\n* dynomite[:web_port] - port for web (HTTP).\n\n= USAGE:\n\nNothing fancy. Include the recipe, or add to a run_list like any other recipe. This cookbook relies on the ruby-dynomite client, which manages dynomite processes. The cookbook will set up dynomite as a runit service.\n\n= LICENSE and AUTHOR:\n\nAuthor:: Joshua Timberman ()\nCopyright:: 2009, Opscode, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "replacing": { + + }, + "dependencies": { + "runit": [ + + ], + "git": [ + + ], + "ruby": [ + + ], + "erlang": [ + + ] + } +} \ No newline at end of file diff --git a/dynomite/metadata.rb b/dynomite/metadata.rb new file mode 100644 index 0000000..8f230a9 --- /dev/null +++ b/dynomite/metadata.rb @@ -0,0 +1,12 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs/Configures dynomite" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.1" + +supports "ubuntu" + +%w{ ruby git erlang runit }.each do |cb| + depends cb +end diff --git a/dynomite/recipes/default.rb b/dynomite/recipes/default.rb new file mode 100644 index 0000000..8546dbd --- /dev/null +++ b/dynomite/recipes/default.rb @@ -0,0 +1,58 @@ +# +# Cookbook Name:: dynomite +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +include_recipe "ruby" +include_recipe "git" +include_recipe "erlang" + +gem_package "rake" +gem_package "open4" + +bash "install_dynomite" do + user "root" + cwd "/tmp" + code <<-EOH + git clone git://github.com/cliffmoon/dynomite.git + cd dynomite + git submodule init + git submodule update + rake clean + rake build_tarball + (cd /usr/local && tar zxf /tmp/dynomite/build/dynomite.tar.tgz) + EOH + not_if { FileTest.exists?("/usr/local/dynomite/bin/dynomite") } +end + +gem_package "dynomite" do + source "http://gems.opscode.com" +end + +directory node[:dynomite][:data_dir] do + recursive true + owner "root" + group "root" + mode "0644" +end + +directory "/var/log/dynomite" do + owner "root" + group "root" + mode "0644" +end + +runit_service "dynomite" diff --git a/dynomite/templates/default/sv-dynomite-log-run.erb b/dynomite/templates/default/sv-dynomite-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/dynomite/templates/default/sv-dynomite-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/dynomite/templates/default/sv-dynomite-run.erb b/dynomite/templates/default/sv-dynomite-run.erb new file mode 100644 index 0000000..459e181 --- /dev/null +++ b/dynomite/templates/default/sv-dynomite-run.erb @@ -0,0 +1,9 @@ +#!/bin/sh +exec 2>&1 +exec /usr/bin/env HOME="/tmp" dynoctl \ + -n <%= @node[:dynomite][:num_nodes] %> \ + -d <%= @node[:dynomite][:data_dir] %> \ + -o <%= @node[:dynomite][:node_name] %> \ + -a <%= @node[:dynomite][:ascii_port] %> \ + -t <%= @node[:dynomite][:thrift_port] %> \ + -h <%= @node[:dynomite][:web_port] %> diff --git a/ec2/attributes/ec2_recipe_options.rb b/ec2/attributes/ec2_recipe_options.rb new file mode 100644 index 0000000..77b23ce --- /dev/null +++ b/ec2/attributes/ec2_recipe_options.rb @@ -0,0 +1,28 @@ +# +# Cookbook Name:: ec2 +# Attribute File:: ec2_recipe_options.rb +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set_unless[:ec2opts][:lvm][:use_ephemeral] = true +set_unless[:ec2opts][:lvm][:ephemeral_mountpoint] = "/mnt" +set_unless[:ec2opts][:lvm][:ephemeral_volume_group] = "ephemeral" +set_unless[:ec2opts][:lvm][:ephemeral_logical_volume] = "store" +set_unless[:ec2opts][:lvm][:ephemeral_devices] = { + "m1.small" => [ "/dev/sda2" ], + "m1.large" => [ "/dev/sdb", "/dev/sdc" ], + "m1.xlarge" => [ "/dev/sdb", "/dev/sdc", "/dev/sdd", "/dev/sde" ], +} diff --git a/ec2/metadata.rb b/ec2/metadata.rb new file mode 100644 index 0000000..d65e4b5 --- /dev/null +++ b/ec2/metadata.rb @@ -0,0 +1,13 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Manage EC2 metadata as attributes" +version "0.9" + +%w{ ubuntu debian}.each do |os| + supports os +end + +attribute "ec2_metadata", + :display_name => "EC2 Metadata", + :description => "Retrieve EC2 instance metadata" diff --git a/ec2/recipes/default.rb b/ec2/recipes/default.rb new file mode 100644 index 0000000..1ef7cc6 --- /dev/null +++ b/ec2/recipes/default.rb @@ -0,0 +1,19 @@ +# +# Cookbook Name:: ec2 +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + diff --git a/ejabberd/metadata.json b/ejabberd/metadata.json new file mode 100755 index 0000000..1882dc3 --- /dev/null +++ b/ejabberd/metadata.json @@ -0,0 +1,38 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "ejabberd": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures ejabberd", + "version": "0.1.0", + "name": "ejabberd", + "providing": { + "ejabberd": [ + + ] + } +} \ No newline at end of file diff --git a/ejabberd/metadata.rb b/ejabberd/metadata.rb new file mode 100755 index 0000000..f9c0619 --- /dev/null +++ b/ejabberd/metadata.rb @@ -0,0 +1,4 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures ejabberd" +version "0.1" diff --git a/ejabberd/recipes/default.rb b/ejabberd/recipes/default.rb new file mode 100755 index 0000000..93385fc --- /dev/null +++ b/ejabberd/recipes/default.rb @@ -0,0 +1,20 @@ +package "ejabberd" + +service "ejabberd" do + action :enable + supports :restart => true +end + +template "/etc/ejabberd/ejabberd.cfg" do + source "ejabberd.cfg.erb" + variables(:jabber_domain => node[:jabber_domain]) + notifies :restart, resources(:service => "ejabberd") +end + +# execute "add ejabberd admin user" do +# command "ejabberdctl register admin #{node[:base][:jabber_domain]} #{node[:base][:jabber_admin_password]}" +# end + +service "ejabberd" do + action :start +end diff --git a/ejabberd/templates/default/ejabberd.cfg.erb b/ejabberd/templates/default/ejabberd.cfg.erb new file mode 100755 index 0000000..db84b1c --- /dev/null +++ b/ejabberd/templates/default/ejabberd.cfg.erb @@ -0,0 +1,197 @@ +%%% +%%% Auto-generated by Chef for <%= @node[:fqdn] %> +%%% + +%%override_global. +%%override_local. +%%override_acls. + + +%% Admin user +{acl, admin, {user, "admin", "<%= @jabber_domain %>"}}. + +%% supported domains +{hosts, ["<%= @jabber_domain %>"]}. + + +%% loglevel: Verbosity of log files generated by ejabberd. +%% 0: No ejabberd log at all (not recommended) +%% 1: Critical +%% 2: Error +%% 3: Warning +%% 4: Info +%% 5: Debug +%% +{loglevel, 4}. + +%% +%% watchdog_admins: If an ejabberd process consumes too much memory, +%% send live notifications to those Jabber accounts. +%% +{watchdog_admins, ["admin@<%= @jabber_domain %>"]}. + + +%% listen: Which ports will ejabberd listen, which service handles it +%% and what options to start it with. +%% +{listen, + [ + {5222, ejabberd_c2s, [ + {access, c2s}, + {shaper, c2s_shaper}, + {max_stanza_size, 65536}, + starttls, {certfile, "/etc/ejabberd/ejabberd.pem"} + ]}, + + {5269, ejabberd_s2s_in, [ + {shaper, s2s_shaper}, + {max_stanza_size, 131072} + ]}, + + {5280, ejabberd_http, [ + http_poll, + web_admin + ]} + ]}. + +%% +%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections. +%% Allowed values are: true or false. +%% You must specify a certificate file. +%% +{s2s_use_starttls, true}. + +%% +%% s2s_certfile: Specify a certificate file. +%% +{s2s_certfile, "/etc/ejabberd/ejabberd.pem"}. + +%% +%% domain_certfile: Specify a different certificate for each served hostname. +%% +%%{domain_certfile, "example.org", "/path/to/example_org.pem"}. +%%{domain_certfile, "example.com", "/path/to/example_com.pem"}. + + +{auth_method, internal}. + + +%% +%% The "normal" shaper limits traffic speed to 1.000 B/s +%% +{shaper, normal, {maxrate, 1000}}. + +%% +%% The "fast" shaper limits traffic speed to 50.000 B/s +%% +{shaper, fast, {maxrate, 50000}}. + + +%% The 'admin' ACL grants administrative privileges to Jabber accounts. +%% You can put as many accounts as you want. +%% +%%{acl, admin, {user, "aleksey", "localhost"}}. +%%{acl, admin, {user, "ermine", "example.org"}}. + +%% +%% Blocked users +%% +%%{acl, blocked, {user, "baduser", "example.org"}}. +%%{acl, blocked, {user, "test"}}. + +%% +%% Local users: don't modify this line. +%% +{acl, local, {user_regexp, ""}}. + + +%% Define the maximum number of time a single user is allowed to connect: +{access, max_user_sessions, [{10, all}]}. + +%% This rule allows access only for local users: +{access, local, [{allow, local}]}. + +%% Only non-blocked users can use c2s connections: +{access, c2s, [{deny, blocked}, + {allow, all}]}. + +%% For all users except admins used "normal" shaper +{access, c2s_shaper, [{none, admin}, + {normal, all}]}. + +%% For all S2S connections used "fast" shaper +{access, s2s_shaper, [{fast, all}]}. + +%% Only admins can send announcement messages: +{access, announce, [{allow, admin}]}. + +%% Only admins can use configuration interface: +{access, configure, [{allow, admin}]}. + +%% Admins of this server are also admins of MUC service: +{access, muc_admin, [{allow, admin}]}. + +%% All users are allowed to use MUC service: +{access, muc, [{allow, all}]}. + +%% No username can be registered via in-band registration: +%% To enable in-band registration, replace 'deny' with 'allow' +% (note that if you remove mod_register from modules list then users will not +% be able to change their password as well as register). +% This setting is default because it's more safe. +{access, register, [{deny, all}]}. + +%% Everybody can create pubsub nodes +{access, pubsub_createnode, [{allow, all}]}. + + +%% language: Default language used for server messages. +%% +{language, "en"}. + +%% Modules enabled in all ejabberd virtual hosts. +%% +{modules, + [ + {mod_adhoc, []}, + {mod_announce, [{access, announce}]}, % requires mod_adhoc + {mod_caps, []}, + {mod_configure,[]}, % requires mod_adhoc + {mod_ctlextra, []}, + {mod_disco, []}, + %%{mod_echo, [{host, "echo.localhost"}]}, + {mod_irc, []}, + {mod_last, []}, + {mod_muc, [ + %%{host, "conference.@HOST@"}, + {access, muc}, + {access_create, muc}, + {access_persistent, muc}, + {access_admin, muc_admin}, + {max_users, 500} + ]}, + %%{mod_muc_log,[]}, + {mod_offline, []}, + {mod_privacy, []}, + {mod_private, []}, + {mod_proxy65, [ + {access, local}, + {shaper, c2s_shaper} + ]}, + {mod_pubsub, [ % requires mod_caps + {access_createnode, pubsub_createnode}, + {plugins, ["default", "pep"]} + ]}, + {mod_roster, []}, + %%{mod_service_log,[]}, + %%{mod_shared_roster,[]}, + {mod_stats, []}, + {mod_time, []}, + {mod_vcard, []}, + {mod_version, []} + ]}. + +%%% Local Variables: +%%% mode: erlang +%%% End: +%%% vim: set filetype=erlang tabstop=8: diff --git a/emacs/metadata.json b/emacs/metadata.json new file mode 100644 index 0000000..ada5bbf --- /dev/null +++ b/emacs/metadata.json @@ -0,0 +1,49 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs emacs", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "emacs": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "centos": [ + + ], + "redhat": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "emacs", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "emacs": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/emacs/metadata.rb b/emacs/metadata.rb new file mode 100644 index 0000000..8d4718f --- /dev/null +++ b/emacs/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs emacs" +version "0.7" + +%w{ ubuntu debian redhat centos }.each do |os| + supports os +end diff --git a/emacs/recipes/default.rb b/emacs/recipes/default.rb new file mode 100644 index 0000000..a7c96de --- /dev/null +++ b/emacs/recipes/default.rb @@ -0,0 +1,21 @@ +# +# Cookbook Name:: emacs +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +package "emacs" do + action :upgrade +end diff --git a/erlang/attributes/erlang.rb b/erlang/attributes/erlang.rb new file mode 100644 index 0000000..cf9c694 --- /dev/null +++ b/erlang/attributes/erlang.rb @@ -0,0 +1 @@ +set_unless[:erlang][:gui_tools] = false diff --git a/erlang/metadata.json b/erlang/metadata.json new file mode 100644 index 0000000..90df1b5 --- /dev/null +++ b/erlang/metadata.json @@ -0,0 +1,43 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs erlang", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "erlang": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "erlang", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "erlang": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/erlang/metadata.rb b/erlang/metadata.rb new file mode 100644 index 0000000..1f912ef --- /dev/null +++ b/erlang/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs erlang" +version "0.7" + +%w{ ubuntu debian }.each do |os| + supports os +end diff --git a/erlang/recipes/default.rb b/erlang/recipes/default.rb new file mode 100644 index 0000000..d3626ab --- /dev/null +++ b/erlang/recipes/default.rb @@ -0,0 +1,29 @@ +# Cookbook Name:: erlang +# Recipe:: default +# Author:: Joe Williams +# +# Copyright 2008-2009, Joe Williams +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node[:platform] +when "debian", "ubuntu" + erlpkg = node[:erlang][:gui_tools] ? "erlang" : "erlang-nox" + package erlpkg + package "erlang-dev" + package "erlang-manpages" +else + package "erlang" +end + diff --git a/fail2ban/metadata.json b/fail2ban/metadata.json new file mode 100644 index 0000000..a944db8 --- /dev/null +++ b/fail2ban/metadata.json @@ -0,0 +1,43 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs and configures fail2ban", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "fail2ban": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "fail2ban", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "fail2ban": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/fail2ban/metadata.rb b/fail2ban/metadata.rb new file mode 100644 index 0000000..ae74758 --- /dev/null +++ b/fail2ban/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures fail2ban" +version "0.7" + +%w{ ubuntu debian }.each do |os| + supports os +end diff --git a/fail2ban/recipes/default.rb b/fail2ban/recipes/default.rb new file mode 100644 index 0000000..5f07d42 --- /dev/null +++ b/fail2ban/recipes/default.rb @@ -0,0 +1,35 @@ +# +# Cookbook Name:: fail2ban +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +package "fail2ban" do + action :upgrade +end + +service "fail2ban" do + action :enable +end + +%w{ fail2ban jail }.each do |cfg| + template "/etc/fail2ban/#{cfg}.conf" do + source "#{cfg}.conf.erb" + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "fail2ban") + end +end diff --git a/fail2ban/templates/default/fail2ban.conf.erb b/fail2ban/templates/default/fail2ban.conf.erb new file mode 100644 index 0000000..598cc01 --- /dev/null +++ b/fail2ban/templates/default/fail2ban.conf.erb @@ -0,0 +1,34 @@ +# Fail2Ban configuration file +# +# Author: Cyril Jaquier +# +# $Revision: 494 $ +# + +[Definition] + +# Option: loglevel +# Notes.: Set the log level output. +# 1 = ERROR +# 2 = WARN +# 3 = INFO +# 4 = DEBUG +# Values: NUM Default: 3 +# +loglevel = 3 + +# Option: logtarget +# Notes.: Set the log target. This could be a file, SYSLOG, STDERR or STDOUT. +# Only one log target can be specified. +# Values: STDOUT STDERR SYSLOG file Default: /var/log/fail2ban.log +# +logtarget = /var/log/fail2ban.log + +# Option: socket +# Notes.: Set the socket file. This is used to communicate with the daemon. Do +# not remove this file when Fail2ban runs. It will not be possible to +# communicate with the server afterwards. +# Values: FILE Default: /tmp/fail2ban.sock +# +socket = /tmp/fail2ban.sock + diff --git a/fail2ban/templates/default/jail.conf.erb b/fail2ban/templates/default/jail.conf.erb new file mode 100644 index 0000000..658ec78 --- /dev/null +++ b/fail2ban/templates/default/jail.conf.erb @@ -0,0 +1,202 @@ +# Fail2Ban configuration file. +# +# This file was composed for Debian systems from the original one +# provided now under /usr/share/doc/fail2ban/examples/jail.conf +# for additional examples. +# +# To avoid merges during upgrades DO NOT MODIFY THIS FILE +# and rather provide your changes in /etc/fail2ban/jail.local +# +# Author: Yaroslav O. Halchenko +# +# $Revision: 281 $ +# + +# The DEFAULT allows a global definition of the options. They can be override +# in each jail afterwards. + +[DEFAULT] + +# "ignoreip" can be an IP address, a CIDR mask or a DNS host +ignoreip = 127.0.0.1 192.168.100.0 +bantime = 300 +maxretry = 5 + +# "backend" specifies the backend used to get files modification. Available +# options are "gamin", "polling" and "auto". +# yoh: For some reason Debian shipped python-gamin didn't work as expected +# This issue left ToDo, so polling is default backend for now +backend = polling + +# +# Destination email address used solely for the interpolations in +# jail.{conf,local} configuration files. +destemail = root@localhost + +# +# ACTIONS +# + +# Default banning action (e.g. iptables, iptables-new, +# iptables-multiport, shorewall, etc) It is used to define +# action_* variables. Can be overriden globally or per +# section within jail.local file +banaction = iptables-multiport + + +# +# Action shortcuts. To be used to define action parameter + +# The simplest action to take: ban only +action_ = %(banaction)s[name=%(__name__)s, port="%(port)s"] + +# ban & send an e-mail with whois report to the destemail. +action_mw = %(banaction)s[name=%(__name__)s, port="%(port)s"] + mail-whois[name=%(__name__)s, dest="%(destemail)s"] + +# ban & send an e-mail with whois report and relevant log lines +# to the destemail. +action_mwl = %(banaction)s[name=%(__name__)s, port="%(port)s"] + mail-whois-lines[name=%(__name__)s, dest="%(destemail)s", logpath=%(logpath)s] + +# Choose default action. To change, just override value of 'action' with the +# interpolation to the chosen action shortcut (e.g. action_mw, action_mwl, etc) in jail.local +# globally (section [DEFAULT]) or per specific section +action = %(action_)s + +# +# JAILS +# + +# Next jails corresponds to the standard configuration in Fail2ban 0.6 which +# was shipped in Debian. Please enable any defined here jail by including +# +# [SECTION_NAME] +# enabled = true +# +# in /etc/fail2ban/jail.local. +# +# Optionally you may override any other parameter (e.g. banaction, +# action, port, logpath, etc) in that section within jail.local + +[ssh] + +enabled = true +port = ssh,sftp +filter = sshd +logpath = /var/log/auth.log +maxretry = 6 + + +[ssh-ddos] + +enabled = false +port = ssh,sftp +filter = sshd-ddos +logpath = /var/log/auth.log +maxretry = 6 + +# +# HTTP servers +# + +[apache] + +enabled = false +port = http,https +filter = apache-auth +logpath = /var/log/apache*/*access.log +maxretry = 6 + +# default action is now multiport, so apache-multiport jail was left +# for compatibility with previous (<0.7.6-2) releases +[apache-multiport] + +enabled = false +port = http,https +filter = apache-auth +logpath = /var/log/apache*/*access.log +maxretry = 6 + +[apache-noscript] + +enabled = false +port = http,https +filter = apache-noscript +logpath = /var/log/apache*/*error.log +maxretry = 6 + +# +# FTP servers +# + +[vsftpd] + +enabled = false +port = ftp,ftp-data,ftps,ftps-data +filter = vsftpd +logpath = /var/log/vsftpd.log +# or overwrite it in jails.local to be +# logpath = /var/log/auth.log +# if you want to rely on PAM failed login attempts +# vsftpd's failregex should match both of those formats +maxretry = 6 + + +[proftpd] + +enabled = false +port = ftp,ftp-data,ftps,ftps-data +filter = proftpd +logpath = /var/log/proftpd/proftpd.log +maxretry = 6 + + +[wuftpd] + +enabled = false +port = ftp,ftp-data,ftps,ftps-data +filter = wuftpd +logpath = /var/log/auth.log +maxretry = 6 + + +# +# Mail servers +# + +[postfix] + +enabled = false +port = smtp,ssmtp +filter = postfix +logpath = /var/log/maillog + + +[couriersmtp] + +enabled = false +port = smtp,ssmtp +filter = couriersmtp +logpath = /var/log/maillog + + +# +# Mail servers authenticators: might be used for smtp,ftp,imap servers, so +# all relevant ports get banned +# + +[courierauth] + +enabled = false +port = smtp,ssmtp,imap2,imap3,imaps,pop3,pop3s +filter = courierlogin +logpath = /var/log/maillog + + +[sasl] + +enabled = false +port = smtp,ssmtp,imap2,imap3,imaps,pop3,pop3s +filter = sasl +logpath = /var/log/maillog diff --git a/ganglia/attributes/ganglia.rb b/ganglia/attributes/ganglia.rb new file mode 100755 index 0000000..149b038 --- /dev/null +++ b/ganglia/attributes/ganglia.rb @@ -0,0 +1,5 @@ +default[:ganglia][:grid_name] = 'Ganglia' +default[:ganglia][:server_fqdn] = 'ganglia' +default[:ganglia][:cluster_name] = 'Default' +default[:ganglia][:servers] = [ '1.2.3.4:3737', '5.6.7.8:3737' ] +default[:ganglia][:clusters] = [ { :name => 'Default', :port => 1234, :interval => 15 } ] diff --git a/ganglia/metadata.rb b/ganglia/metadata.rb new file mode 100755 index 0000000..eee8d73 --- /dev/null +++ b/ganglia/metadata.rb @@ -0,0 +1,5 @@ +maintainer "ganglia" +maintainer_email "sysadmins@37signals.com" +description "Configures Ganglia" +version "0.1" +depends "apache2" diff --git a/ganglia/recipes/client.rb b/ganglia/recipes/client.rb new file mode 100755 index 0000000..c006eeb --- /dev/null +++ b/ganglia/recipes/client.rb @@ -0,0 +1,22 @@ + +package "ganglia-monitor" + +service "ganglia-monitor" do + enabled true + running true + pattern "gmond" +end + +template "/etc/ganglia/gmond.conf" do + source "gmond.conf.erb" + backup false + owner "ganglia" + group "ganglia" + mode 0644 + variables(:server => false, + :cluster => { + :name => @node[:ganglia][:cluster_name], + :port => @node[:ganglia][:clusters].find { |c| c[:name] == @node[:ganglia][:cluster_name] }[:port] + }) + notifies :restart, resources(:service => "ganglia-monitor") +end diff --git a/ganglia/recipes/default.rb b/ganglia/recipes/default.rb new file mode 100755 index 0000000..8c8ee49 --- /dev/null +++ b/ganglia/recipes/default.rb @@ -0,0 +1,8 @@ +# +# Cookbook Name:: ganglia +# Recipe:: default +# +# Copyright 2009, 37signals +# +# All rights reserved - Do Not Redistribute +# diff --git a/ganglia/recipes/server.rb b/ganglia/recipes/server.rb new file mode 100755 index 0000000..fe9298f --- /dev/null +++ b/ganglia/recipes/server.rb @@ -0,0 +1,38 @@ + +include_recipe "apache2" + +package "ganglia-webfrontend" +package "gmetad" + +template "/etc/ganglia/vhost.conf" do + source "ganglia-vhost.conf.erb" + backup false + owner "root" + group "www-data" + mode 0640 +end + +apache_site "ganglia" do + config_path "/etc/ganglia/vhost.conf" +end + +service "gmetad" do + enabled true +end + +cluster_nodes = {} +search(:node, '*', %w(fqdn ganglia_cluster_name)) do |node| + next unless node['ganglia_cluster_name'] + cluster_nodes[node['ganglia_cluster_name']] ||= [] + cluster_nodes[node['ganglia_cluster_name']] << node['fqdn'].split('.').first +end + +template "/etc/ganglia/gmetad.conf" do + source "gmetad.conf.erb" + backup false + owner "ganglia" + group "ganglia" + mode 0644 + variables(:cluster_nodes => cluster_nodes) + notifies :restart, resources(:service => "gmetad") +end diff --git a/ganglia/templates/default/ganglia-vhost.conf.erb b/ganglia/templates/default/ganglia-vhost.conf.erb new file mode 100755 index 0000000..7e2ecf9 --- /dev/null +++ b/ganglia/templates/default/ganglia-vhost.conf.erb @@ -0,0 +1,6 @@ + + ServerName <%= @node[:ganglia][:server_fqdn] %> + ServerAlias <%= @node[:ganglia][:server_fqdn].split('.').first %> + DocumentRoot /usr/share/ganglia-webfrontend + ErrorLog /var/log/apache2/ganglia_error.log + diff --git a/ganglia/templates/default/gmetad.conf.erb b/ganglia/templates/default/gmetad.conf.erb new file mode 100755 index 0000000..77d922d --- /dev/null +++ b/ganglia/templates/default/gmetad.conf.erb @@ -0,0 +1,117 @@ +# This is an example of a Ganglia Meta Daemon configuration file +# http://ganglia.sourceforge.net/ +# +# $Id: gmetad.conf.in 1639 2008-08-09 23:30:32Z carenas $ +# +#------------------------------------------------------------------------------- +# Setting the debug_level to 1 will keep daemon in the forground and +# show only error messages. Setting this value higher than 1 will make +# gmetad output debugging information and stay in the foreground. +# default: 0 +# debug_level 10 +# +#------------------------------------------------------------------------------- +# What to monitor. The most important section of this file. +# +# The data_source tag specifies either a cluster or a grid to +# monitor. If we detect the source is a cluster, we will maintain a complete +# set of RRD databases for it, which can be used to create historical +# graphs of the metrics. If the source is a grid (it comes from another gmetad), +# we will only maintain summary RRDs for it. +# +# Format: +# data_source "my cluster" [polling interval] address1:port addreses2:port ... +# +# The keyword 'data_source' must immediately be followed by a unique +# string which identifies the source, then an optional polling interval in +# seconds. The source will be polled at this interval on average. +# If the polling interval is omitted, 15sec is asssumed. +# +# A list of machines which service the data source follows, in the +# format ip:port, or name:port. If a port is not specified then 8649 +# (the default gmond port) is assumed. +# default: There is no default value +# +# data_source "my cluster" 10 localhost my.machine.edu:8649 1.2.3.5:8655 +# data_source "my grid" 50 1.3.4.7:8655 grid.org:8651 grid-backup.org:8651 +# data_source "another source" 1.3.4.7:8655 1.3.4.8 + +<% @node[:ganglia][:clusters].each do |cluster| %> +<% next unless @cluster_nodes[cluster[:name]] %> +data_source "<%= cluster[:name] %>" <%= cluster[:interval] %> <%= @cluster_nodes[cluster[:name]].join(' ') %> +<% end %> + +# +# Round-Robin Archives +# You can specify custom Round-Robin archives here (defaults are listed below) +# +# RRAs "RRA:AVERAGE:0.5:1:244" "RRA:AVERAGE:0.5:24:244" "RRA:AVERAGE:0.5:168:244" "RRA:AVERAGE:0.5:672:244" \ +# "RRA:AVERAGE:0.5:5760:374" +# + +# +#------------------------------------------------------------------------------- +# Scalability mode. If on, we summarize over downstream grids, and respect +# authority tags. If off, we take on 2.5.0-era behavior: we do not wrap our output +# in tags, we ignore all tags we see, and always assume +# we are the "authority" on data source feeds. This approach does not scale to +# large groups of clusters, but is provided for backwards compatibility. +# default: on +# scalable off +# +#------------------------------------------------------------------------------- +# The name of this Grid. All the data sources above will be wrapped in a GRID +# tag with this name. +# default: unspecified +gridname "<%= @node[:ganglia][:grid_name] %>" + +#------------------------------------------------------------------------------- +# The authority URL for this grid. Used by other gmetads to locate graphs +# for our data sources. Generally points to a ganglia/ +# website on this machine. +# default: "http://hostname/ganglia/", +# where hostname is the name of this machine, as defined by gethostname(). +# authority "http://mycluster.org/newprefix/" +# +#------------------------------------------------------------------------------- +# List of machines this gmetad will share XML with. Localhost +# is always trusted. +# default: There is no default value +# trusted_hosts 127.0.0.1 169.229.50.165 my.gmetad.org +# +#------------------------------------------------------------------------------- +# If you want any host which connects to the gmetad XML to receive +# data, then set this value to "on" +# default: off +# all_trusted on +# +#------------------------------------------------------------------------------- +# If you don't want gmetad to setuid then set this to off +# default: on +# setuid off +# +#------------------------------------------------------------------------------- +# User gmetad will setuid to (defaults to "nobody") +# default: "nobody" +setuid_username "ganglia" +# +#------------------------------------------------------------------------------- +# The port gmetad will answer requests for XML +# default: 8651 +# xml_port 8651 +# +#------------------------------------------------------------------------------- +# The port gmetad will answer queries for XML. This facility allows +# simple subtree and summation views of the XML tree. +# default: 8652 +# interactive_port 8652 +# +#------------------------------------------------------------------------------- +# The number of threads answering XML requests +# default: 4 +# server_threads 10 +# +#------------------------------------------------------------------------------- +# Where gmetad stores its round-robin databases +# default: "/var/lib/ganglia/rrds" +# rrd_rootdir "/var/lib/ganglia/rrds" diff --git a/ganglia/templates/default/gmond.conf.erb b/ganglia/templates/default/gmond.conf.erb new file mode 100755 index 0000000..ce0be51 --- /dev/null +++ b/ganglia/templates/default/gmond.conf.erb @@ -0,0 +1,336 @@ +/* This configuration is as close to 2.5.x default behavior as possible + The values closely match ./gmond/metric.h definitions in 2.5.x */ +globals { + daemonize = yes + setuid = yes + user = ganglia + debug_level = 0 + max_udp_msg_len = 1472 + mute = no + deaf = no + host_dmax = 0 /*secs */ + cleanup_threshold = 300 /*secs */ + gexec = no + send_metadata_interval = 0 +} + +/* If a cluster attribute is specified, then all gmond hosts are wrapped inside + * of a tag. If you do not specify a cluster tag, then all will + * NOT be wrapped inside of a tag. */ +cluster { + name = "<%= @node[:ganglia][:cluster_name] %>" + owner = "unspecified" + latlong = "unspecified" + url = "unspecified" +} + +/* The host section describes attributes of the host, like the location */ +host { + location = "unspecified" +} + +/* Feel free to specify as many udp_send_channels as you like. Gmond + used to only support having a single channel */ +udp_send_channel { + mcast_join = 239.2.11.71 + port = <%= @cluster[:port] %> +} + +/* You can specify as many udp_recv_channels as you like as well. */ +udp_recv_channel { + mcast_join = 239.2.11.71 + port = <%= @cluster[:port] %> + bind = 239.2.11.71 +} + +/* You can specify as many tcp_accept_channels as you like to share + an xml description of the state of the cluster */ +tcp_accept_channel { + port = 8649 +} + +/* Each metrics module that is referenced by gmond must be specified and + loaded. If the module has been statically linked with gmond, it does not + require a load path. However all dynamically loadable modules must include + a load path. */ +modules { + module { + name = "core_metrics" + } + module { + name = "cpu_module" + path = "/usr/lib/ganglia/modcpu.so" + } + module { + name = "disk_module" + path = "/usr/lib/ganglia/moddisk.so" + } + module { + name = "load_module" + path = "/usr/lib/ganglia/modload.so" + } + module { + name = "mem_module" + path = "/usr/lib/ganglia/modmem.so" + } + module { + name = "net_module" + path = "/usr/lib/ganglia/modnet.so" + } + module { + name = "proc_module" + path = "/usr/lib/ganglia/modproc.so" + } + module { + name = "sys_module" + path = "/usr/lib/ganglia/modsys.so" + } +} + +include ('/etc/ganglia/conf.d/*.conf') + + +/* The old internal 2.5.x metric array has been replaced by the following + collection_group directives. What follows is the default behavior for + collecting and sending metrics that is as close to 2.5.x behavior as + possible. */ + +/* This collection group will cause a heartbeat (or beacon) to be sent every + 20 seconds. In the heartbeat is the GMOND_STARTED data which expresses + the age of the running gmond. */ +collection_group { + collect_once = yes + time_threshold = 20 + metric { + name = "heartbeat" + } +} + +/* This collection group will send general info about this host every 1200 secs. + This information doesn't change between reboots and is only collected once. */ +collection_group { + collect_once = yes + time_threshold = 1200 + metric { + name = "cpu_num" + title = "CPU Count" + } + metric { + name = "cpu_speed" + title = "CPU Speed" + } + metric { + name = "mem_total" + title = "Memory Total" + } + /* Should this be here? Swap can be added/removed between reboots. */ + metric { + name = "swap_total" + title = "Swap Space Total" + } + metric { + name = "boottime" + title = "Last Boot Time" + } + metric { + name = "machine_type" + title = "Machine Type" + } + metric { + name = "os_name" + title = "Operating System" + } + metric { + name = "os_release" + title = "Operating System Release" + } + metric { + name = "location" + title = "Location" + } +} + +/* This collection group will send the status of gexecd for this host every 300 secs */ +/* Unlike 2.5.x the default behavior is to report gexecd OFF. */ +collection_group { + collect_once = yes + time_threshold = 300 + metric { + name = "gexec" + title = "Gexec Status" + } +} + +/* This collection group will collect the CPU status info every 20 secs. + The time threshold is set to 90 seconds. In honesty, this time_threshold could be + set significantly higher to reduce unneccessary network chatter. */ +collection_group { + collect_every = 20 + time_threshold = 90 + /* CPU status */ + metric { + name = "cpu_user" + value_threshold = "1.0" + title = "CPU User" + } + metric { + name = "cpu_system" + value_threshold = "1.0" + title = "CPU System" + } + metric { + name = "cpu_idle" + value_threshold = "5.0" + title = "CPU Idle" + } + metric { + name = "cpu_nice" + value_threshold = "1.0" + title = "CPU Nice" + } + metric { + name = "cpu_aidle" + value_threshold = "5.0" + title = "CPU aidle" + } + metric { + name = "cpu_wio" + value_threshold = "1.0" + title = "CPU wio" + } + /* The next two metrics are optional if you want more detail... + ... since they are accounted for in cpu_system. + metric { + name = "cpu_intr" + value_threshold = "1.0" + title = "CPU intr" + } + metric { + name = "cpu_sintr" + value_threshold = "1.0" + title = "CPU sintr" + } + */ +} + +collection_group { + collect_every = 20 + time_threshold = 90 + /* Load Averages */ + metric { + name = "load_one" + value_threshold = "1.0" + title = "One Minute Load Average" + } + metric { + name = "load_five" + value_threshold = "1.0" + title = "Five Minute Load Average" + } + metric { + name = "load_fifteen" + value_threshold = "1.0" + title = "Fifteen Minute Load Average" + } +} + +/* This group collects the number of running and total processes */ +collection_group { + collect_every = 80 + time_threshold = 950 + metric { + name = "proc_run" + value_threshold = "1.0" + title = "Total Running Processes" + } + metric { + name = "proc_total" + value_threshold = "1.0" + title = "Total Processes" + } +} + +/* This collection group grabs the volatile memory metrics every 40 secs and + sends them at least every 180 secs. This time_threshold can be increased + significantly to reduce unneeded network traffic. */ +collection_group { + collect_every = 40 + time_threshold = 180 + metric { + name = "mem_free" + value_threshold = "1024.0" + title = "Free Memory" + } + metric { + name = "mem_shared" + value_threshold = "1024.0" + title = "Shared Memory" + } + metric { + name = "mem_buffers" + value_threshold = "1024.0" + title = "Memory Buffers" + } + metric { + name = "mem_cached" + value_threshold = "1024.0" + title = "Cached Memory" + } + metric { + name = "swap_free" + value_threshold = "1024.0" + title = "Free Swap Space" + } +} + +collection_group { + collect_every = 40 + time_threshold = 300 + metric { + name = "bytes_out" + value_threshold = 4096 + title = "Bytes Sent" + } + metric { + name = "bytes_in" + value_threshold = 4096 + title = "Bytes Received" + } + metric { + name = "pkts_in" + value_threshold = 256 + title = "Packets Received" + } + metric { + name = "pkts_out" + value_threshold = 256 + title = "Packets Sent" + } +} + +/* Different than 2.5.x default since the old config made no sense */ +collection_group { + collect_every = 1800 + time_threshold = 3600 + metric { + name = "disk_total" + value_threshold = 1.0 + title = "Total Disk Space" + } +} + +collection_group { + collect_every = 40 + time_threshold = 180 + metric { + name = "disk_free" + value_threshold = 1.0 + title = "Disk Space Available" + } + metric { + name = "part_max_used" + value_threshold = 1.0 + title = "Maximum Disk Space Used" + } +} + diff --git a/gems/README.rdoc b/gems/README.rdoc new file mode 100644 index 0000000..e0c1e40 --- /dev/null +++ b/gems/README.rdoc @@ -0,0 +1,31 @@ += DESCRIPTION: + +Installs a set of gems + += REQUIREMENTS: + +== Cookbooks: + +* ruby_enterprise + += ATTRIBUTES: + +* gems - array os hashes with :name, :version and :source for the gem + += LICENSE and AUTHOR: + +Author:: Fabio Akita () +Copyright:: 2010, Prodigus Consulting. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/gems/attributes/gems.rb b/gems/attributes/gems.rb new file mode 100644 index 0000000..1e8a1d9 --- /dev/null +++ b/gems/attributes/gems.rb @@ -0,0 +1 @@ +set_unless[:gems] = [{ :name => "rake" }] \ No newline at end of file diff --git a/gems/metadata.json b/gems/metadata.json new file mode 100644 index 0000000..855d8dd --- /dev/null +++ b/gems/metadata.json @@ -0,0 +1,40 @@ +{ + "suggestions": { + + }, + "conflicting": { + + }, + "providing": { + "gems": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nInstalls a set of gems\n\n= REQUIREMENTS:\n\n== Cookbooks:\n\n* ruby_enterprise\n\n= ATTRIBUTES:\n\n* gems - array os hashes with :name, :version and :source for the gem\n\n= LICENSE and AUTHOR:\n\nAuthor:: Fabio Akita ()\nCopyright:: 2010, Prodigus Consulting.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n", + "recipes": { + "gems": "" + }, + "attributes": { + + }, + "replacing": { + + }, + "dependencies": { + "ruby_enterprise": [ + + ] + }, + "platforms": { + + }, + "maintainer": "Prodigus Consulting", + "description": "Installs/Configures gems", + "name": "gems", + "version": "0.1.0", + "recommendations": { + + }, + "maintainer_email": "fabioakita@gmail.com" +} \ No newline at end of file diff --git a/gems/metadata.rb b/gems/metadata.rb new file mode 100644 index 0000000..3f41e54 --- /dev/null +++ b/gems/metadata.rb @@ -0,0 +1,7 @@ +maintainer "Prodigus Consulting" +maintainer_email "fabioakita@gmail.com" +license "Apache 2.0" +description "Installs/Configures gems" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.1" +depends "ruby_enterprise" diff --git a/gems/recipes/default.rb b/gems/recipes/default.rb new file mode 100644 index 0000000..d9d8951 --- /dev/null +++ b/gems/recipes/default.rb @@ -0,0 +1,29 @@ +# +# Cookbook Name:: gems +# Recipe:: default +# +# Copyright 2010, Prodigus Consulting +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +node[:gems].each do |gem| + gem_package gem[:name] do + if gem[:version] && !gem[:version].empty? + version gem[:version] + end + if gem[:source] + source gem[:source] + end + action :install + end +end \ No newline at end of file diff --git a/git/README.rdoc b/git/README.rdoc new file mode 100644 index 0000000..8bfa17c --- /dev/null +++ b/git/README.rdoc @@ -0,0 +1,37 @@ += DESCRIPTION: + +Installs git. + += REQUIREMENTS: + +== Cookbooks: + +Opscode Cookbooks (http://github.com/opscode/cookbooks/tree/master) + +* runit + += USAGE: + +This cookbook primarily installs git core packages. It can also be used to serve git repositories. + + include_recipe "git::server" + +This creates the directory /srv/git and starts a git daemon, exporting all repositories found. Repositories need to be added manually, but will be available once they are created. + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () + +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/git/metadata.json b/git/metadata.json new file mode 100644 index 0000000..a435277 --- /dev/null +++ b/git/metadata.json @@ -0,0 +1,52 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs git and\/or sets up a Git server daemon", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "git::server": "Sets up a runit_service for git daemon", + "git": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "git", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "git::server": [ + + ], + "git": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nInstalls git.\n\n= REQUIREMENTS:\n\n== Cookbooks:\n\nOpscode Cookbooks (http:\/\/github.com\/opscode\/cookbooks\/tree\/master)\n\n* runit\n\n= USAGE:\n\nThis cookbook primarily installs git core packages. It can also be used to serve git repositories.\n\n include_recipe \"git::server\"\n\nThis creates the directory \/srv\/git and starts a git daemon, exporting all repositories found. Repositories need to be added manually, but will be available once they are created.\n\n= LICENSE and AUTHOR:\n \nAuthor:: Joshua Timberman ()\n\nCopyright:: 2009, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.", + "replacing": { + + }, + "dependencies": { + "runit": [ + + ], + "apache2": [ + + ] + } +} \ No newline at end of file diff --git a/git/metadata.rb b/git/metadata.rb new file mode 100644 index 0000000..465b217 --- /dev/null +++ b/git/metadata.rb @@ -0,0 +1,15 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs git and/or sets up a Git server daemon" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.7" +recipe "git::server", "Sets up a runit_service for git daemon" + +%w{ ubuntu debian }.each do |os| + supports os +end + +%w{ apache2 runit }.each do |cb| + depends cb +end diff --git a/git/recipes/default.rb b/git/recipes/default.rb new file mode 100644 index 0000000..36b2121 --- /dev/null +++ b/git/recipes/default.rb @@ -0,0 +1,26 @@ +# +# Cookbook Name:: git +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +case node[:platform] +when "debian", "ubuntu" + package "git-core" +else + package "git" +end + +package "git-email" diff --git a/git/recipes/server.rb b/git/recipes/server.rb new file mode 100644 index 0000000..8edd5de --- /dev/null +++ b/git/recipes/server.rb @@ -0,0 +1,28 @@ +# +# Cookbook Name:: git +# Recipe:: server +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include_recipe "git" +include_recipe "runit" + +directory "/srv/git" do + owner "root" + group "root" + mode 0755 +end + +runit_service "git-daemon" diff --git a/git/templates/default/sv-git-daemon-log-run.erb b/git/templates/default/sv-git-daemon-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/git/templates/default/sv-git-daemon-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/git/templates/default/sv-git-daemon-run.erb b/git/templates/default/sv-git-daemon-run.erb new file mode 100644 index 0000000..3f3e53d --- /dev/null +++ b/git/templates/default/sv-git-daemon-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec /usr/bin/git daemon --export-all --user nobody --group daemon --base-path=/srv/git /srv/git diff --git a/gitosis/files/default/access.py b/gitosis/files/default/access.py new file mode 100755 index 0000000..a41bc63 --- /dev/null +++ b/gitosis/files/default/access.py @@ -0,0 +1,102 @@ +import os, logging +import re, fnmatch +from ConfigParser import NoSectionError, NoOptionError + +from gitosis import group + +def isWildcardAccessible(path, repos, validpats=re.compile(r"\*|\?|\[.*?\]")): + """ + Check if path is accessible with wildcards. + + Only wildcards containing *, ? or [] are accepted. + """ + for repo in repos: + if not validpats.search(repo): + continue + if fnmatch.fnmatch(path, repo): + return True + return False + +def haveAccess(config, user, mode, path): + """ + Map request for write access to allowed path. + + Note for read-only access, the caller should check for write + access too. + + Returns ``None`` for no access, or a tuple of toplevel directory + containing repositories and a relative path to the physical repository. + """ + log = logging.getLogger('gitosis.access.haveAccess') + + log.debug( + 'Access check for %(user)r as %(mode)r on %(path)r...' + % dict( + user=user, + mode=mode, + path=path, + )) + + basename, ext = os.path.splitext(path) + if ext == '.git': + log.debug( + 'Stripping .git suffix from %(path)r, new value %(basename)r' + % dict( + path=path, + basename=basename, + )) + path = basename + + for groupname in group.getMembership(config=config, user=user): + try: + repos = config.get('group %s' % groupname, mode) + except (NoSectionError, NoOptionError): + repos = [] + else: + repos = repos.split() + + mapping = None + + if path in repos or isWildcardAccessible(path, repos): + log.debug( + 'Access ok for %(user)r as %(mode)r on %(path)r' + % dict( + user=user, + mode=mode, + path=path, + )) + mapping = path + else: + try: + mapping = config.get('group %s' % groupname, + 'map %s %s' % (mode, path)) + except (NoSectionError, NoOptionError): + pass + else: + log.debug( + 'Access ok for %(user)r as %(mode)r on %(path)r=%(mapping)r' + % dict( + user=user, + mode=mode, + path=path, + mapping=mapping, + )) + + if mapping is not None: + prefix = None + try: + prefix = config.get( + 'group %s' % groupname, 'repositories') + except (NoSectionError, NoOptionError): + try: + prefix = config.get('gitosis', 'repositories') + except (NoSectionError, NoOptionError): + prefix = 'repositories' + + log.debug( + 'Using prefix %(prefix)r for %(path)r' + % dict( + prefix=prefix, + path=mapping, + )) + return (prefix, mapping) diff --git a/gitosis/files/default/serve.py b/gitosis/files/default/serve.py new file mode 100755 index 0000000..d62fdd8 --- /dev/null +++ b/gitosis/files/default/serve.py @@ -0,0 +1,207 @@ +""" +Enforce git-shell to only serve allowed by access control policy. +directory. The client should refer to them without any extra directory +prefix. Repository names are forced to match ALLOW_RE. +""" + +import logging + +import sys, os, re + +from gitosis import access +from gitosis import repository +from gitosis import gitweb +from gitosis import gitdaemon +from gitosis import app +from gitosis import util + +log = logging.getLogger('gitosis.serve') + +ALLOW_RE = re.compile("^'/*(?P[a-zA-Z0-9][a-zA-Z0-9@._-]*(/[a-zA-Z0-9][a-zA-Z0-9@._-]*)*)'$") + +COMMANDS_READONLY = [ + 'git-upload-pack', + 'git upload-pack', + ] + +COMMANDS_WRITE = [ + 'git-receive-pack', + 'git receive-pack', + ] + +class ServingError(Exception): + """Serving error""" + + def __str__(self): + return '%s' % self.__doc__ + +class CommandMayNotContainNewlineError(ServingError): + """Command may not contain newline""" + +class UnknownCommandError(ServingError): + """Unknown command denied""" + +class UnsafeArgumentsError(ServingError): + """Arguments to command look dangerous""" + +class AccessDenied(ServingError): + """Access denied to repository""" + +class WriteAccessDenied(AccessDenied): + """Repository write access denied""" + +class ReadAccessDenied(AccessDenied): + """Repository read access denied""" + +def serve( + cfg, + user, + command, + ): + if '\n' in command: + raise CommandMayNotContainNewlineError() + + try: + verb, args = command.split(None, 1) + except ValueError: + # all known "git-foo" commands take one argument; improve + # if/when needed + raise UnknownCommandError() + + if verb == 'git': + try: + subverb, args = args.split(None, 1) + except ValueError: + # all known "git foo" commands take one argument; improve + # if/when needed + raise UnknownCommandError() + verb = '%s %s' % (verb, subverb) + + if (verb not in COMMANDS_WRITE + and verb not in COMMANDS_READONLY): + raise UnknownCommandError() + + match = ALLOW_RE.match(args) + if match is None: + raise UnsafeArgumentsError() + + path = match.group('path') + + # write access is always sufficient + newpath = access.haveAccess( + config=cfg, + user=user, + mode='writable', + path=path) + + if newpath is None: + # didn't have write access; try once more with the popular + # misspelling + newpath = access.haveAccess( + config=cfg, + user=user, + mode='writeable', + path=path) + if newpath is not None: + log.warning( + 'Repository %r config has typo "writeable", ' + +'should be "writable"', + path, + ) + + if newpath is None: + # didn't have write access + + newpath = access.haveAccess( + config=cfg, + user=user, + mode='readonly', + path=path) + + if newpath is None: + raise ReadAccessDenied() + + if verb in COMMANDS_WRITE: + # didn't have write access and tried to write + raise WriteAccessDenied() + + (topdir, relpath) = newpath + assert not relpath.endswith('.git'), \ + 'git extension should have been stripped: %r' % relpath + repopath = '%s.git' % relpath + fullpath = os.path.join(topdir, repopath) + if (not os.path.exists(fullpath) + and verb in COMMANDS_WRITE): + # it doesn't exist on the filesystem, but the configuration + # refers to it, we're serving a write request, and the user is + # authorized to do that: create the repository on the fly + + # create leading directories + p = topdir + for segment in repopath.split(os.sep)[:-1]: + p = os.path.join(p, segment) + util.mkdir(p, 0750) + + repository.init(path=fullpath) + gitweb.set_descriptions( + config=cfg, + ) + generated = util.getGeneratedFilesDir(config=cfg) + gitweb.generate_project_list( + config=cfg, + path=os.path.join(generated, 'projects.list'), + ) + gitdaemon.set_export_ok( + config=cfg, + ) + + # put the verb back together with the new path + newcmd = "%(verb)s '%(path)s'" % dict( + verb=verb, + path=fullpath, + ) + return newcmd + +class Main(app.App): + def create_parser(self): + parser = super(Main, self).create_parser() + parser.set_usage('%prog [OPTS] USER') + parser.set_description( + 'Allow restricted git operations under DIR') + return parser + + def handle_args(self, parser, cfg, options, args): + try: + (user,) = args + except ValueError: + parser.error('Missing argument USER.') + + main_log = logging.getLogger('gitosis.serve.main') + os.umask(0022) + + cmd = os.environ.get('SSH_ORIGINAL_COMMAND', None) + if cmd is None: + main_log.error('Need SSH_ORIGINAL_COMMAND in environment.') + sys.exit(1) + + main_log.debug('Got command %(cmd)r' % dict( + cmd=cmd, + )) + + os.chdir(os.path.expanduser('~')) + + try: + newcmd = serve( + cfg=cfg, + user=user, + command=cmd, + ) + except ServingError, e: + main_log.error('%s', e) + sys.exit(1) + + main_log.debug('Serving %s', newcmd) + os.environ['GITOSIS_USER'] = user + os.execvp('git-shell', ['git-shell', '-c', newcmd]) + main_log.error('Cannot execute git-shell.') + sys.exit(1) diff --git a/gitosis/metadata.json b/gitosis/metadata.json new file mode 100755 index 0000000..6dbc8c8 --- /dev/null +++ b/gitosis/metadata.json @@ -0,0 +1,38 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "gitosis": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures Gitosis", + "version": "0.1.0", + "name": "gitosis", + "providing": { + "gitosis": [ + + ] + } +} \ No newline at end of file diff --git a/gitosis/metadata.rb b/gitosis/metadata.rb new file mode 100755 index 0000000..deaaa30 --- /dev/null +++ b/gitosis/metadata.rb @@ -0,0 +1,4 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures Gitosis" +version "0.1" diff --git a/gitosis/recipes/default.rb b/gitosis/recipes/default.rb new file mode 100755 index 0000000..eb25891 --- /dev/null +++ b/gitosis/recipes/default.rb @@ -0,0 +1,20 @@ + +package "gitosis" + +user "gitosis" do + action :remove +end + +group "gitosis" do + action :remove +end + +# Install local modifications to Gitosis +%W(access.py serve.py).each do |patched| + remote_file "/usr/share/python-support/gitosis/gitosis-0.2-py2.5.egg/gitosis/#{patched}" do + source "#{patched}" + owner "root" + group "root" + mode 0755 + end +end diff --git a/gitweb/attributes/gitweb.rb b/gitweb/attributes/gitweb.rb new file mode 100755 index 0000000..e6af5b0 --- /dev/null +++ b/gitweb/attributes/gitweb.rb @@ -0,0 +1 @@ +default.gitweb[:config_path] = "/etc/gitweb" \ No newline at end of file diff --git a/gitweb/metadata.json b/gitweb/metadata.json new file mode 100755 index 0000000..a1cfe66 --- /dev/null +++ b/gitweb/metadata.json @@ -0,0 +1,43 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "gitweb": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + "git": [ + + ], + "apache2": [ + + ] + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures gitweb", + "version": "0.1.0", + "name": "gitweb", + "providing": { + "gitweb": [ + + ] + } +} \ No newline at end of file diff --git a/gitweb/metadata.rb b/gitweb/metadata.rb new file mode 100755 index 0000000..e8c23b4 --- /dev/null +++ b/gitweb/metadata.rb @@ -0,0 +1,6 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures gitweb" +version "0.1" +depends "apache2" +depends "git" \ No newline at end of file diff --git a/gitweb/recipes/default.rb b/gitweb/recipes/default.rb new file mode 100755 index 0000000..5e9ce82 --- /dev/null +++ b/gitweb/recipes/default.rb @@ -0,0 +1,18 @@ +require_recipe "git::server" + +package "gitweb" + +directory node[:gitweb][:config_path] do + mode 0755 + recursive true +end + +template "/etc/gitweb/apache.conf" + +template "/etc/gitweb/projects.conf" do + variables :projects => node[:git][:repos] +end + +apache_site "gitweb" do + config_path "/etc/gitweb/apache.conf" +end \ No newline at end of file diff --git a/gitweb/templates/default/apache-vhost.conf.erb b/gitweb/templates/default/apache-vhost.conf.erb new file mode 100755 index 0000000..0026e09 --- /dev/null +++ b/gitweb/templates/default/apache-vhost.conf.erb @@ -0,0 +1,32 @@ + + + DocumentRoot /usr/share/gitweb + ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ + + SSLEngine on + SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem + SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key + + RewriteEngine on + + # support gitc-style URLs + + RewriteRule ^/c/(.+)/(.+)$ /gitweb/$1.git?a=commitdiff&h=$2 [R,L] + + RewriteRule ^/gitweb/([a-zA-Z0-9_\/-]+\.git)/?(\?.*)?$ /cgi-bin/gitweb.cgi/$1 [L,PT] + + Alias /gitweb /usr/share/gitweb + + + Options Indexes FollowSymlinks ExecCGI + DirectoryIndex /cgi-bin/gitweb.cgi + AllowOverride None + + + + Order allow,deny + Allow from all + AllowOverride None + + + \ No newline at end of file diff --git a/gitweb/templates/default/apache.conf.erb b/gitweb/templates/default/apache.conf.erb new file mode 100755 index 0000000..0026e09 --- /dev/null +++ b/gitweb/templates/default/apache.conf.erb @@ -0,0 +1,32 @@ + + + DocumentRoot /usr/share/gitweb + ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ + + SSLEngine on + SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem + SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key + + RewriteEngine on + + # support gitc-style URLs + + RewriteRule ^/c/(.+)/(.+)$ /gitweb/$1.git?a=commitdiff&h=$2 [R,L] + + RewriteRule ^/gitweb/([a-zA-Z0-9_\/-]+\.git)/?(\?.*)?$ /cgi-bin/gitweb.cgi/$1 [L,PT] + + Alias /gitweb /usr/share/gitweb + + + Options Indexes FollowSymlinks ExecCGI + DirectoryIndex /cgi-bin/gitweb.cgi + AllowOverride None + + + + Order allow,deny + Allow from all + AllowOverride None + + + \ No newline at end of file diff --git a/gitweb/templates/default/gitweb.conf.erb b/gitweb/templates/default/gitweb.conf.erb new file mode 100755 index 0000000..2e67548 --- /dev/null +++ b/gitweb/templates/default/gitweb.conf.erb @@ -0,0 +1,26 @@ +# path to git projects (.git) +$projectroot = "<%= @node[:git][:repo_root] %>"; + +# directory to use for temp files +$git_temp = "/tmp"; + +# target of the home link on top of all pages +#$home_link = $my_uri || "/"; + +# html text to include at home page +$home_text = "indextext.html"; + +# file with project list; by default, simply scan the projectroot dir. +$projects_list = "/etc/gitweb/projects.conf"; + +# stylesheet to use +$stylesheet = "/gitweb.css"; + +# logo to use +$logo = "/git-logo.png"; + +# the 'favicon' +$favicon = "/git-favicon.png"; + +# restrict access to projects on visible overview page +$strict_export = true; \ No newline at end of file diff --git a/gitweb/templates/default/projects.conf.erb b/gitweb/templates/default/projects.conf.erb new file mode 100755 index 0000000..b8d9ee6 --- /dev/null +++ b/gitweb/templates/default/projects.conf.erb @@ -0,0 +1,4 @@ +<% @projects.each do |name, conf| %> +<% next if conf[:visible] == false %> +<%= name %> Gitosis+User +<% end %> \ No newline at end of file diff --git a/glassfish/README.rdoc b/glassfish/README.rdoc new file mode 100644 index 0000000..8d77480 --- /dev/null +++ b/glassfish/README.rdoc @@ -0,0 +1,8 @@ += DESCRIPTION: + += REQUIREMENTS: + += ATTRIBUTES: + += USAGE: + diff --git a/glassfish/attributes/glassfish.rb b/glassfish/attributes/glassfish.rb new file mode 100644 index 0000000..686309f --- /dev/null +++ b/glassfish/attributes/glassfish.rb @@ -0,0 +1,82 @@ +# +# Cookbook Name:: glassfish +# Attribute File:: glassfish +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +#openInstaller Dry Run Answer File. This File can be used as input to the openInstaller engine using the -a option. + +# unis system user +set_unless[:glassfish][:systemuser]="glassfish" +#unix system group +set_unless[:glassfish][:systemgroup]="glassfish" +# fetch_url +set_unless[:glassfish][:fetch_url]="http://download.java.net/glassfish/v3-prelude/promoted/glassfish-v3-prelude-b28f-unix.sh" +#InstallHome.directory.INSTALL_HOME= +set_unless[:glassfish][:INSTALL_HOME]="/opt/glassfishv3-prelude" +#License.license.ACCEPT_LICENSE=0 +set_unless[:glassfish][:ACCEPT_LICENSE]="0" +#RegistrationOptions.regoptions.CREATE_NEWACCT=CREATE_NEWACCT +set_unless[:glassfish][:CREATE_NEWACCT]="CREATE_NEWACCT" +#RegistrationOptions.regoptions.DUMMY_PROP= +set_unless[:glassfish][:DUMMY_PROP]="" +#RegistrationOptions.regoptions.SKIP_REGISTRATION=SKIP_REGISTRATION +set_unless[:glassfish][:SKIP_REGISTRATION]="SKIP_REGISTRATION" +#RegistrationOptions.regoptions.USERNAME= +set_unless[:glassfish][:USERNAME]="" +#RegistrationOptions.regoptions.USERPASSWORD= +set_unless[:glassfish][:USERPASSWORD]="" +#RegistrationOptions.regoptions.USE_EXISTINGACCT=USE_EXISTINGACCT +set_unless[:glassfish][:USE_EXISTINGACCT]="USE_EXISTINGACCT" +#SOAccountCreation.accountinfo.COMPANYNAME= +set_unless[:glassfish][:COMPANYNAME]="" +#SOAccountCreation.accountinfo.COUNTRY= +set_unless[:glassfish][:COUNTRY]="" +#SOAccountCreation.accountinfo.COUNTRY_DROP_DOWN= +set_unless[:glassfish][:COUNTRY_DROP_DOWN]="" +#SOAccountCreation.accountinfo.EMAIL= +set_unless[:glassfish][:EMAIL]="" +#SOAccountCreation.accountinfo.FIRSTNAME= +set_unless[:glassfish][:FIRSTNAME]="" +#SOAccountCreation.accountinfo.LASTNAME= +set_unless[:glassfish][:LASTNAME]="" +#SOAccountCreation.accountinfo.PASSWORD= +set_unless[:glassfish][:PASSWORD]="" +#SOAccountCreation.accountinfo.REENTERPASSWORD= +set_unless[:glassfish][:REENTERPASSWORD]="" +#glassfish.Administration.ADMIN_PASSWORD=adminadmin +set_unless[:glassfish][:ADMIN_PASSWORD]="adminadmin" +#glassfish.Administration.ADMIN_PORT=4848 +set_unless[:glassfish][:ADMIN_PORT]="4848" +#glassfish.Administration.ADMIN_USER=admin +set_unless[:glassfish][:ADMIN_USER]="admin" +#glassfish.Administration.ANONYMOUS=ANONYMOUS +set_unless[:glassfish][:ANONYMOUS]="ANONYMOUS" +#glassfish.Administration.LOGIN_MODE=true +set_unless[:glassfish][:LOGIN_MODE]="true" +#glassfish.Administration.HTTP_PORT=8080 +set_unless[:glassfish][:HTTP_PORT]="8081" +# Can be set to anonymous or non_anonymous, to require administrator to log in with user name and password. +# glassfish.Administration.NON_ANONYMOUS=NON_ANONYMOUS +set_unless[:glassfish][:NON_ANONYMOUS]="NON_ANONYMOUS" +#updatetool.Configuration.ALLOW_UPDATE_CHECK=true +set_unless[:glassfish][:ALLOW_UPDATE_CHECK]="false" +#updatetool.Configuration.BOOTSTRAP_UPDATETOOL=true +set_unless[:glassfish][:BOOTSTRAP_UPDATETOOL]="false" +#updatetool.Configuration.PROXY_HOST= +set_unless[:glassfish][:PROXY_HOST]= "" +#updatetool.Configuration.PROXY_PORT= +set_unless[:glassfish][:PROXY_PORT]= "" + diff --git a/glassfish/metadata.json b/glassfish/metadata.json new file mode 100644 index 0000000..2b9713e --- /dev/null +++ b/glassfish/metadata.json @@ -0,0 +1,50 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs\/Configures Glassfish", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "glassfish": "Main Glassfish configuration", + "glassfish": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "centos": [ + + ], + "debian": [ + + ], + "redhat": [ + + ] + }, + "version": "0.1.0", + "name": "glassfish", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "glassfish": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\n= REQUIREMENTS:\n\n= ATTRIBUTES: \n\n= USAGE:\n\n", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/glassfish/metadata.rb b/glassfish/metadata.rb new file mode 100644 index 0000000..3a782db --- /dev/null +++ b/glassfish/metadata.rb @@ -0,0 +1,11 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs/Configures Glassfish" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.1" +recipe "glassfish", "Main Glassfish configuration" + +%w{redhat centos debian ubuntu}.each do |os| + supports os +end diff --git a/glassfish/recipes/default.rb b/glassfish/recipes/default.rb new file mode 100644 index 0000000..b07e4dd --- /dev/null +++ b/glassfish/recipes/default.rb @@ -0,0 +1,73 @@ +# +# Cookbook Name:: glassfish +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +group node[:glassfish][:systemgroup] do +end + +user node[:glassfish][:systemuser] do + comment "SUN Glassfish" + gid node[:glassfish][:systemgroup] + home node[:glassfish][:INSTALL_HOME] + shell "/bin/sh" +end + +remote_file "/tmp/glassfish.sh" do + owner node[:glassfish][:systemuser] + source node[:glassfish][:fetch_url] + mode "0744" + checksum "6d4a20f14de" +end + +answer_file = "/tmp/v3-prelude-answer" + +template answer_file do + owner node[:glassfish][:systemuser] + source "answer_file.erb" +end + +directory node[:glassfish][:INSTALL_HOME] do + owner node[:glassfish][:systemuser] + group node[:glassfish][:systemgroup] + mode "0755" + action :create + recursive true +end + +execute "install-glassfish" do + command "/tmp/glassfish.sh -a #{answer_file} -s" + creates File.join(node[:glassfish][:INSTALL_HOME],"uninstall.sh") + user node[:glassfish][:systemuser] + action :run +end + +file answer_file do + action :delete +end + +template "/etc/init.d/glassfish" do + source "glassfish-init.d-script.erb" + mode "0755" +end + +service "glassfish" do + supports :start => true, :restart => true, :stop => true + action [ :enable, :start ] +end + diff --git a/glassfish/templates/default/answer_file.erb b/glassfish/templates/default/answer_file.erb new file mode 100644 index 0000000..4880d89 --- /dev/null +++ b/glassfish/templates/default/answer_file.erb @@ -0,0 +1,27 @@ +InstallHome.directory.INSTALL_HOME=<%= @node[:glassfish][:INSTALL_HOME] %> +License.license.ACCEPT_LICENSE=<%= @node[:glassfish][:ACCEPT_LICENSE] %> +RegistrationOptions.regoptions.CREATE_NEWACCT=<%= @node[:glassfish][:CREATE_NEWACCT] %> +RegistrationOptions.regoptions.DUMMY_PROP=<%= @node[:glassfish][:DUMMY_PROP] %> +RegistrationOptions.regoptions.SKIP_REGISTRATION=<%= @node[:glassfish][:SKIP_REGISTRATION] %> +RegistrationOptions.regoptions.USERNAME=<%= @node[:glassfish][:USERNAME] %> +RegistrationOptions.regoptions.USERPASSWORD=<%= @node[:glassfish][:USERPASSWORD] %> +RegistrationOptions.regoptions.USE_EXISTINGACCT=<%= @node[:glassfish][:USE_EXISTINGACCT] %> +SOAccountCreation.accountinfo.COMPANYNAME=<%= @node[:glassfish][:COMPANYNAME] %> +SOAccountCreation.accountinfo.COUNTRY=<%= @node[:glassfish][:COUNTRY] %> +SOAccountCreation.accountinfo.COUNTRY_DROP_DOWN=<%= @node[:glassfish][:COUNTRY_DROP_DOWN] %> +SOAccountCreation.accountinfo.EMAIL=<%= @node[:glassfish][:EMAIL] %> +SOAccountCreation.accountinfo.FIRSTNAME=<%= @node[:glassfish][:FIRSTNAME] %> +SOAccountCreation.accountinfo.LASTNAME=<%= @node[:glassfish][:LASTNAME] %> +SOAccountCreation.accountinfo.PASSWORD=<%= @node[:glassfish][:PASSWORD] %> +SOAccountCreation.accountinfo.REENTERPASSWORD=<%= @node[:glassfish][:REENTERPASSWORD] %> +glassfish.Administration.ADMIN_PASSWORD=<%= @node[:glassfish][:ADMIN_PASSWORD] %> +glassfish.Administration.ADMIN_PORT=<%= @node[:glassfish][:ADMIN_PORT] %> +glassfish.Administration.ADMIN_USER=<%= @node[:glassfish][:ADMIN_USER] %> +glassfish.Administration.ANONYMOUS=<%= @node[:glassfish][:ANONYMOUS] %> +glassfish.Administration.LOGIN_MODE=<%= @node[:glassfish][:LOGIN_MODE] %> +glassfish.Administration.HTTP_PORT=<%= @node[:glassfish][:HTTP_PORT] %> +glassfish.Administration.NON_ANONYMOUS=<%= @node[:glassfish][:NON_ANONYMOUS] %> +updatetool.Configuration.ALLOW_UPDATE_CHECK=<%= @node[:glassfish][:ALLOW_UPDATE_CHECK] %> +updatetool.Configuration.BOOTSTRAP_UPDATETOOL=<%= @node[:glassfish][:BOOTSTRAP_UPDATETOOL] %> +updatetool.Configuration.PROXY_HOST=<%= @node[:glassfish][:PROXY_HOST] %> +updatetool.Configuration.PROXY_PORT=<%= @node[:glassfish][:PROXY_PORT] %> diff --git a/glassfish/templates/default/glassfish-init.d-script.erb b/glassfish/templates/default/glassfish-init.d-script.erb new file mode 100644 index 0000000..f402c77 --- /dev/null +++ b/glassfish/templates/default/glassfish-init.d-script.erb @@ -0,0 +1,44 @@ +#!/bin/bash +# +# glassfish: Startup script for Glassfish Application Server. +# +# chkconfig: 3 80 05 +# description: Startup script for domain1 of Glassfish Application Server. + +GLASSFISH_HOME=<%= File.join(@node[:glassfish][:INSTALL_HOME],"glassfish") %> +export GLASSFISH_HOME + +GLASSFISH_OWNER=<%= @node[:glassfish][:systemuser] %> ; +export GLASSFISH_OWNER + +start() { + echo -n "Starting Glassfish: " + echo "Starting Glassfish at `date`" >> $GLASSFISH_HOME/domains/domain1/logs/startup.log + su $GLASSFISH_OWNER -c "$GLASSFISH_HOME/bin/asadmin start-domain domain1" >> $GLASSFISH_HOME/domains/domain1/logs/startup.log + sleep 2 + echo "done" +} + +stop() { + echo -n "Stopping Glassfish: " + echo "Stopping Glassfish at `date`" >> $GLASSFISH_HOME/domains/domain1/logs/startup.log + su $GLASSFISH_OWNER -c "$GLASSFISH_HOME/bin/asadmin stop-domain domain1" >> $GLASSFISH_HOME/domains/domain1/logs/startup.log + echo "done" +} + +# See how we were called. +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart) + stop + start + ;; + *) + echo $"Usage: glassfish {start|stop|restart}" + exit +esac diff --git a/gnupg/metadata.json b/gnupg/metadata.json new file mode 100755 index 0000000..2635d3a --- /dev/null +++ b/gnupg/metadata.json @@ -0,0 +1,38 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "gnupg": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures gnupg", + "version": "0.1.0", + "name": "gnupg", + "providing": { + "gnupg": [ + + ] + } +} \ No newline at end of file diff --git a/gnupg/metadata.rb b/gnupg/metadata.rb new file mode 100755 index 0000000..472e01d --- /dev/null +++ b/gnupg/metadata.rb @@ -0,0 +1,4 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures gnupg" +version "0.1" diff --git a/gnupg/recipes/default.rb b/gnupg/recipes/default.rb new file mode 100755 index 0000000..f8af316 --- /dev/null +++ b/gnupg/recipes/default.rb @@ -0,0 +1 @@ +package "gnupg" diff --git a/god/README.rdoc b/god/README.rdoc new file mode 100644 index 0000000..2a86aa0 --- /dev/null +++ b/god/README.rdoc @@ -0,0 +1,49 @@ += DESCRIPTION: + +Installs god gem, sets up modular configuration directory and provides a define to monitor processes. + += REQUIREMENTS: + +== Platform and Application Environment: + +Tested on Ubuntu 8.10. May work on other platforms, esp Ubuntu/Debian. Sample configuration file uses mongrel_runit for managing mongrels via runit. + +== Cookbooks: + +Opscode cookbooks, http://github.com/opscode/cookbooks/tree/master: + +* ruby +* runit + +Opscode does not yet have a mongrel_runit cookbook. + += ATTRIBUTES: + +No attributes are used. + += USAGE: + +This recipe is designed to be used through the god_monitor define. Create a god configuration file in your application's cookbook and then call god_monitor: + + god_monitor "myproj" do + config "myproj.god.erb" + end + +A sample mongrel.god.erb is provided, though it assumes mongrel_runit is used. This can be used as a baseline for customization. + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/god/definitions/god_monitor.rb b/god/definitions/god_monitor.rb new file mode 100644 index 0000000..1a13c25 --- /dev/null +++ b/god/definitions/god_monitor.rb @@ -0,0 +1,38 @@ +# +# Cookbook Name:: god +# Definition:: god_monitor +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :god_monitor, :config => "mongrel.god.erb", :max_memory => 100, :cpu => 50 do + include_recipe "god" + + template "/etc/god/conf.d/#{params[:name]}.god" do + source params[:config] + owner "root" + group "root" + mode 0644 + variables( + :name => params[:name], + :max_memory => params[:max_memory], + :cpu => params[:cpu], + :sv_bin => node[:runit][:sv_bin], + :params => params + ) + notifies :restart, resources(:service => "god") + end + +end diff --git a/god/metadata.json b/god/metadata.json new file mode 100644 index 0000000..5b47dc8 --- /dev/null +++ b/god/metadata.json @@ -0,0 +1,48 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs and configures god and provides a define for monitoring", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "god": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "god", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "god": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nInstalls god gem, sets up modular configuration directory and provides a define to monitor processes.\n\n= REQUIREMENTS:\n\n== Platform and Application Environment:\n\nTested on Ubuntu 8.10. May work on other platforms, esp Ubuntu\/Debian. Sample configuration file uses mongrel_runit for managing mongrels via runit. \n\n== Cookbooks:\n\nOpscode cookbooks, http:\/\/github.com\/opscode\/cookbooks\/tree\/master:\n\n* ruby\n* runit\n\nOpscode does not yet have a mongrel_runit cookbook.\n\n= ATTRIBUTES: \n\nNo attributes are used.\n\n= USAGE:\n\nThis recipe is designed to be used through the god_monitor define. Create a god configuration file in your application's cookbook and then call god_monitor:\n\n god_monitor \"myproj\" do\n config \"myproj.god.erb\"\n end\n\nA sample mongrel.god.erb is provided, though it assumes mongrel_runit is used. This can be used as a baseline for customization.\n\n= LICENSE and AUTHOR:\n \nAuthor:: Joshua Timberman ()\nCopyright:: 2009, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "replacing": { + + }, + "dependencies": { + "runit": [ + + ], + "ruby": [ + + ] + } +} \ No newline at end of file diff --git a/god/metadata.rb b/god/metadata.rb new file mode 100644 index 0000000..0657f8f --- /dev/null +++ b/god/metadata.rb @@ -0,0 +1,14 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures god and provides a define for monitoring" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.7" + +%w{debian ubuntu}.each do |os| + supports os +end + +%w{ ruby runit }.each do |cb| + depends cb +end diff --git a/god/recipes/default.rb b/god/recipes/default.rb new file mode 100644 index 0000000..7d98392 --- /dev/null +++ b/god/recipes/default.rb @@ -0,0 +1,41 @@ +# +# Cookbook Name:: god +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "ruby" +include_recipe "runit" + +gem_package "god" do + action :install +end + +directory "/etc/god/conf.d" do + recursive true + owner "root" + group "root" + mode 0755 +end + +template "/etc/god/master.god" do + source "master.god.erb" + owner "root" + group "root" + mode 0755 +end + +runit_service "god" diff --git a/god/templates/default/master.god.erb b/god/templates/default/master.god.erb new file mode 100644 index 0000000..2836258 --- /dev/null +++ b/god/templates/default/master.god.erb @@ -0,0 +1,20 @@ +# +# Generated by Chef for <%= @node[:hostname] %> +# +God.load "/etc/god/conf.d/*.god" + +God::Contacts::Email.message_settings = { + :from => 'god@<%= @node[:domain] %>' +} + +God::Contacts::Email.server_settings = { + :address => "localhost", + :port => 25, + :domain => "<%= @node[:domain] %>", +} + +God.contact(:email) do |c| + c.name = 'ops' + c.email = 'ops@<%= @node[:domain] %>' +end + diff --git a/god/templates/default/mongrel.god.erb b/god/templates/default/mongrel.god.erb new file mode 100644 index 0000000..742e6fa --- /dev/null +++ b/god/templates/default/mongrel.god.erb @@ -0,0 +1,40 @@ +# Generated by Chef + +mongrel_list = Array.new +Dir["/etc/sv/mongrel-<%= @name %>-*"].each do |file| + file =~ /mongrel-(.+)-(\d+)/ + group = $1 + port = $2 + mongrel = Hash.new + mongrel[:name] = "#{group}-#{port}" + mongrel[:start] = "<%= @sv_bin %> start #{file}" + mongrel[:stop] = "<%= @sv_bin %> stop #{file}" + mongrel[:restart] = "<%= @sv_bin %> restart #{file}" + mongrel[:pidfile] = File.join(file, "supervise", "pid") + mongrel[:port] = port + mongrel_list << mongrel +end + +mongrel_list.each do |mongrel| + God.watch do |w| + w.name = mongrel[:name] + w.interval = 30.seconds # default + w.start = mongrel[:start] + w.stop = mongrel[:stop] + w.restart = mongrel[:restart] + w.start_grace = 10.seconds + w.restart_grace = 10.seconds + w.pid_file = mongrel[:pidfile] + + w.restart_if do |restart| + restart.condition(:memory_usage) do |c| + c.above = <%= @max_memory %>.megabytes + end + + restart.condition(:cpu_usage) do |c| + c.above = <%= @cpu %>.percent + c.times = 5 + end + end + end +end diff --git a/god/templates/default/sv-god-log-run.erb b/god/templates/default/sv-god-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/god/templates/default/sv-god-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/god/templates/default/sv-god-run.erb b/god/templates/default/sv-god-run.erb new file mode 100644 index 0000000..b3c31d4 --- /dev/null +++ b/god/templates/default/sv-god-run.erb @@ -0,0 +1,6 @@ +#!/bin/sh +exec 2>&1 + +trap 'kill -HUP %1' 1 2 13 15 + +/usr/bin/god -D -c /etc/god/master.god --no-syslog & wait diff --git a/hadoop/README.rdoc b/hadoop/README.rdoc new file mode 100644 index 0000000..e9e1e6b --- /dev/null +++ b/hadoop/README.rdoc @@ -0,0 +1,63 @@ += DESCRIPTION: + +Installs Apache hadoop and sets up a basic distributed cluster per the quick start documentation. + += REQUIREMENTS: + +== Platform: + +Tested on Ubuntu 8.10, though should work on most Linux distributions, see hadoop[:java_home]. + +== Cookbooks: + +Opscode cookbooks, http://github.com/opscode/cookbooks/tree/master: + +* java + += ATTRIBUTES: + +* hadoop[:mirror_url] - Get a mirror from http://www.apache.org/dyn/closer.cgi/hadoop/core/. +* hadoop[:version] - Specify the version of hadoop to install. +* hadoop[:uid] - Default userid of the hadoop user. +* hadoop[:gid] - Default group for the hadoop user. +* hadoop[:java_home] - You will probably want to change this to match where Java is installed on your platform. + +You may wish to add more attributes for tuning the configuration file templates. + += USAGE: + +This cookbook performs the tasks described in the Hadoop Quick Start[1] to get the software installed. You should copy this to a site-cookbook and modify the templates to meet your requirements. + +Once the recipe is run, the distributed filesystem can be formated using the script /usr/bin/hadoop. + + sudo -u hadoop /usr/bin/hadoop namenode -format + +You may need to set up SSH keys for hadoop management commands. + +Note that this is not the 'default' config per se, so using the start-all.sh script won't start the processes because the config files live elsewhere. For running various hadoop processes as services, we suggest runit. A sample 'run' script is provided. The HADOOP_LOG_DIR in the run script must exist for each process. These could be wrapped in a define. + +* datanode +* jobtracker +* namenode +* tasktracker + + +[1] http://hadoop.apache.org/core/docs/current/quickstart.html + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () + +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/hadoop/metadata.json b/hadoop/metadata.json new file mode 100644 index 0000000..b6f5243 --- /dev/null +++ b/hadoop/metadata.json @@ -0,0 +1,61 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs hadoop and sets up basic cluster per Cloudera's quick start docs", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "hadoop::doc": "", + "hadoop::pig": "", + "hadoop": "", + "hadoop::hive": "", + "hadoop::conf_pseudo": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.8.0", + "name": "hadoop", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "hadoop::doc": [ + + ], + "hadoop::pig": [ + + ], + "hadoop::hive": [ + + ], + "hadoop": [ + + ], + "hadoop::conf_pseudo": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nInstalls Apache hadoop and sets up a basic distributed cluster per the quick start documentation.\n\n= REQUIREMENTS:\n\n== Platform:\n\nTested on Ubuntu 8.10, though should work on most Linux distributions, see hadoop[:java_home].\n\n== Cookbooks:\n\nOpscode cookbooks, http:\/\/github.com\/opscode\/cookbooks\/tree\/master:\n\n* java\n\n= ATTRIBUTES: \n\n* hadoop[:mirror_url] - Get a mirror from http:\/\/www.apache.org\/dyn\/closer.cgi\/hadoop\/core\/.\n* hadoop[:version] - Specify the version of hadoop to install.\n* hadoop[:uid] - Default userid of the hadoop user.\n* hadoop[:gid] - Default group for the hadoop user.\n* hadoop[:java_home] - You will probably want to change this to match where Java is installed on your platform.\n\nYou may wish to add more attributes for tuning the configuration file templates.\n\n= USAGE:\n\nThis cookbook performs the tasks described in the Hadoop Quick Start[1] to get the software installed. You should copy this to a site-cookbook and modify the templates to meet your requirements. \n\nOnce the recipe is run, the distributed filesystem can be formated using the script \/usr\/bin\/hadoop. \n\n sudo -u hadoop \/usr\/bin\/hadoop namenode -format\n \nYou may need to set up SSH keys for hadoop management commands. \n\nNote that this is not the 'default' config per se, so using the start-all.sh script won't start the processes because the config files live elsewhere. For running various hadoop processes as services, we suggest runit. A sample 'run' script is provided. The HADOOP_LOG_DIR in the run script must exist for each process. These could be wrapped in a define. \n\n* datanode\n* jobtracker\n* namenode\n* tasktracker\n\n\n[1] http:\/\/hadoop.apache.org\/core\/docs\/current\/quickstart.html\n\n= LICENSE and AUTHOR:\n \nAuthor:: Joshua Timberman ()\n\nCopyright:: 2009, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.", + "replacing": { + + }, + "dependencies": { + "java": [ + + ] + } +} \ No newline at end of file diff --git a/hadoop/metadata.rb b/hadoop/metadata.rb new file mode 100644 index 0000000..29d7bc4 --- /dev/null +++ b/hadoop/metadata.rb @@ -0,0 +1,11 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs hadoop and sets up basic cluster per Cloudera's quick start docs" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.8" +depends "java" + +%w{ debian ubuntu }.each do |os| + supports os +end diff --git a/hadoop/recipes/conf_pseudo.rb b/hadoop/recipes/conf_pseudo.rb new file mode 100644 index 0000000..65d4406 --- /dev/null +++ b/hadoop/recipes/conf_pseudo.rb @@ -0,0 +1,29 @@ +# +# Cookbook Name:: hadoop +# Recipe:: conf_pseudo +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "hadoop" + +package "hadoop-conf-pseudo" + +%w{namenode secondarynamenode datanode jobtracker tasktracker}.each do |d| + service "hadoop-#{d}" do + action [ :start, :enable ] + end +end + diff --git a/hadoop/recipes/default.rb b/hadoop/recipes/default.rb new file mode 100644 index 0000000..2104f95 --- /dev/null +++ b/hadoop/recipes/default.rb @@ -0,0 +1,38 @@ +# +# Cookbook Name:: hadoop +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "java" + +execute "apt-get update" do + action :nothing +end + +template "/etc/apt/sources.list.d/cloudera.list" do + owner "root" + mode "0644" + source "cloudera.list.erb" + notifies :run, resources("execute[apt-get update]"), :immediately +end + +execute "curl -s http://archive.cloudera.com/debian/archive.key | apt-key add -" do + not_if "apt-key export 'Cloudera Apt Repository'" +end + +package "hadoop" + diff --git a/hadoop/recipes/doc.rb b/hadoop/recipes/doc.rb new file mode 100644 index 0000000..47cb0ba --- /dev/null +++ b/hadoop/recipes/doc.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: hadoop +# Recipe:: doc +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "java" + +package "hadoop-doc" diff --git a/hadoop/recipes/hive.rb b/hadoop/recipes/hive.rb new file mode 100644 index 0000000..df71304 --- /dev/null +++ b/hadoop/recipes/hive.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: hadoop +# Recipe:: hive +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "hadoop" + +package "hive" diff --git a/hadoop/recipes/pig.rb b/hadoop/recipes/pig.rb new file mode 100644 index 0000000..65285b0 --- /dev/null +++ b/hadoop/recipes/pig.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: hadoop +# Recipe:: pig +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "hadoop" + +package "pig" diff --git a/hadoop/templates/default/cloudera.list.erb b/hadoop/templates/default/cloudera.list.erb new file mode 100644 index 0000000..ab9e5b8 --- /dev/null +++ b/hadoop/templates/default/cloudera.list.erb @@ -0,0 +1,2 @@ +deb http://archive.cloudera.com/debian <%= @node[:lsb][:codename] %> contrib +deb-src http://archive.cloudera.com/debian <%= @node[:lsb][:codename] %> contrib diff --git a/haproxy/README.rdoc b/haproxy/README.rdoc new file mode 100644 index 0000000..a184c78 --- /dev/null +++ b/haproxy/README.rdoc @@ -0,0 +1,37 @@ += DESCRIPTION: + +Installs haproxy and prepares the configuration location. + += REQUIREMENTS: + +== Platform: + +Tested on Ubuntu 8.10. + +== Cookbooks: + += ATTRIBUTES: + +No haproxy-specific attributes are used. + += USAGE: + +Update the haproxy.cfg file with listener(s) for your sites/servers. + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () + +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/haproxy/metadata.json b/haproxy/metadata.json new file mode 100644 index 0000000..77df8ff --- /dev/null +++ b/haproxy/metadata.json @@ -0,0 +1,43 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs and configures haproxy", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "haproxy": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "haproxy", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "haproxy": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nInstalls haproxy and prepares the configuration location.\n\n= REQUIREMENTS:\n\n== Platform:\n\nTested on Ubuntu 8.10.\n\n== Cookbooks:\n\n= ATTRIBUTES: \n\nNo haproxy-specific attributes are used.\n\n= USAGE:\n\nUpdate the haproxy.cfg file with listener(s) for your sites\/servers.\n\n= LICENSE and AUTHOR:\n \nAuthor:: Joshua Timberman ()\n\nCopyright:: 2009, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/haproxy/metadata.rb b/haproxy/metadata.rb new file mode 100644 index 0000000..5fb7c37 --- /dev/null +++ b/haproxy/metadata.rb @@ -0,0 +1,10 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures haproxy" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.7" + +%w{ debian ubuntu }.each do |os| + supports os +end diff --git a/haproxy/recipes/default.rb b/haproxy/recipes/default.rb new file mode 100644 index 0000000..54b7f40 --- /dev/null +++ b/haproxy/recipes/default.rb @@ -0,0 +1,42 @@ +# +# Cookbook Name:: haproxy +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "haproxy" do + action :install +end + +template "/etc/default/haproxy" do + source "haproxy-default.erb" + owner "root" + group "root" + mode 0644 +end + +service "haproxy" do + supports :restart => true, :status => true, :reload => true + action [:enable, :start] +end + +template "/etc/haproxy/haproxy.cfg" do + source "haproxy.cfg.erb" + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "haproxy") +end diff --git a/haproxy/templates/default/haproxy-default.erb b/haproxy/templates/default/haproxy-default.erb new file mode 100644 index 0000000..9a2ee79 --- /dev/null +++ b/haproxy/templates/default/haproxy-default.erb @@ -0,0 +1,4 @@ +# Set ENABLED to 1 if you want the init script to start haproxy. +ENABLED=1 +# Add extra flags here. +#EXTRAOPTS="-de -m 16" diff --git a/haproxy/templates/default/haproxy.cfg.erb b/haproxy/templates/default/haproxy.cfg.erb new file mode 100644 index 0000000..5a09bcd --- /dev/null +++ b/haproxy/templates/default/haproxy.cfg.erb @@ -0,0 +1,27 @@ +global + log 127.0.0.1 local0 + log 127.0.0.1 local1 notice + #log loghost local0 info + maxconn 4096 + #debug + #quiet + user haproxy + group haproxy + +defaults + log global + mode http + option httplog + option dontlognull + retries 3 + redispatch + maxconn 2000 + contimeout 5000 + clitimeout 50000 + srvtimeout 50000 + +# Set up application listeners here. +listen application 0.0.0.0:8400 + balance roundrobin + server localhost 127.0.0.1:4000 weight 1 maxconn 5 check + server localhost 127.0.0.1:4001 weight 1 maxconn 5 check diff --git a/heartbeat/metadata.json b/heartbeat/metadata.json new file mode 100644 index 0000000..fae9edb --- /dev/null +++ b/heartbeat/metadata.json @@ -0,0 +1,45 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs but does not configure heartbeat", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "heartbeat": "" + }, + "suggestions": { + "drbd": [ + + ] + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "heartbeat", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "heartbeat": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/heartbeat/metadata.rb b/heartbeat/metadata.rb new file mode 100644 index 0000000..f6db20a --- /dev/null +++ b/heartbeat/metadata.rb @@ -0,0 +1,10 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs but does not configure heartbeat" +version "0.7" +suggests "drbd" + +%w{ debian ubuntu }.each do |os| + supports os +end diff --git a/heartbeat/recipes/default.rb b/heartbeat/recipes/default.rb new file mode 100644 index 0000000..5d6f5f7 --- /dev/null +++ b/heartbeat/recipes/default.rb @@ -0,0 +1,39 @@ +# +# Cookbook Name:: heartbeat +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# = Requirements +# Templates:: +# * /etc/ha.d/ha.cf +# * /etc/ha.d/haresources +# * /etc/ha.d/authkeys + +%w{ heartbeat-2 heartbeat-2-dev }.each do |pkg| + package pkg do + action :install + end +end + +service "heartbeat" do + supports( + :restart => true, + :status => true + ) + action :enable +end + + diff --git a/hosts/attributes/hosts.rb b/hosts/attributes/hosts.rb new file mode 100755 index 0000000..1f15d2e --- /dev/null +++ b/hosts/attributes/hosts.rb @@ -0,0 +1 @@ +hosts Mash.new unless attribute?(:hosts) \ No newline at end of file diff --git a/hosts/metadata.json b/hosts/metadata.json new file mode 100755 index 0000000..b66e578 --- /dev/null +++ b/hosts/metadata.json @@ -0,0 +1,38 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "hosts": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures hosts", + "version": "0.1.0", + "name": "hosts", + "providing": { + "hosts": [ + + ] + } +} \ No newline at end of file diff --git a/hosts/metadata.rb b/hosts/metadata.rb new file mode 100755 index 0000000..f173c48 --- /dev/null +++ b/hosts/metadata.rb @@ -0,0 +1,4 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures hosts" +version "0.1" diff --git a/hosts/recipes/default.rb b/hosts/recipes/default.rb new file mode 100755 index 0000000..605d213 --- /dev/null +++ b/hosts/recipes/default.rb @@ -0,0 +1,6 @@ +template "/etc/hosts" do + source "hosts.erb" + owner "root" + group "root" + mode 0644 +end diff --git a/hosts/templates/default/hosts.erb b/hosts/templates/default/hosts.erb new file mode 100755 index 0000000..79e257d --- /dev/null +++ b/hosts/templates/default/hosts.erb @@ -0,0 +1,27 @@ +127.0.0.1 localhost <%= @node[:hosts][:localhost_aliases].join(" ") if @node[:hosts][:localhost_aliases] %> +127.0.1.1 <%= @node['fqdn'] %> <%= @node['hostname'] %> <%= "chef.#{@node['domain']}" if @node.recipes.include?("chef::server") %> + +<% if @node[:hosts][:entries] %> +<% @node[:hosts][:entries].each do |h| %> +<%= h.join(" ") %> +<% end %> +<% end %> + +<% if @node[:domain] == "rack-dfw-int.37signals.com" %> +192.168.0.32 queenbee.37signals.com queenbee +192.168.0.5 ob.37signals.com ob +192.168.1.151 basecamp.db.37signals.com +192.168.2.82 basecamp.search.37signals.com +192.168.1.122 backpack.search.37signals.com +192.168.1.120 backpack.db.37signals.com +192.168.1.127 campfire.db.37signals.com +192.168.1.114 dev.37signals.com +<% end %> + +# The following lines are desirable for IPv6 capable hosts +::1 ip6-localhost ip6-loopback +fe00::0 ip6-localnet +ff00::0 ip6-mcastprefix +ff02::1 ip6-allnodes +ff02::2 ip6-allrouters +ff02::3 ip6-allhosts diff --git a/imagemagick/README.rdoc b/imagemagick/README.rdoc new file mode 100644 index 0000000..f750c67 --- /dev/null +++ b/imagemagick/README.rdoc @@ -0,0 +1,37 @@ += DESCRIPTION: + +Installs ImageMagick and optionally Rmagick (RubyGem). + += REQUIREMENTS: + +Should work on RHEL and Debian plus derivatives. + += USAGE: + +To install just ImageMagick, + + include_recipe "imagemagick" + +In your own recipe/cookbook. To install the RubyGem rmagick, + + include_recipe "imagemagick::rmagick" + +Which will install imagemagick, as well as the development libraries for imagemagick (so rmagick can be built). + += LICENSE: + +Author:: Joshua Timberman () + +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/imagemagick/metadata.json b/imagemagick/metadata.json new file mode 100644 index 0000000..63253af --- /dev/null +++ b/imagemagick/metadata.json @@ -0,0 +1,56 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs\/Configures imagemagick", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "imagemagick::rmagick": "", + "imagemagick": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "rhel": [ + + ], + "fedora": [ + + ], + "centos": [ + + ], + "debian": [ + + ] + }, + "version": "0.1.0", + "name": "imagemagick", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "imagemagick::rmagick": [ + + ], + "imagemagick": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nInstalls ImageMagick and optionally Rmagick (RubyGem).\n\n= REQUIREMENTS:\n\nShould work on RHEL and Debian plus derivatives.\n\n= USAGE:\n\nTo install just ImageMagick,\n\n include_recipe \"imagemagick\"\n \nIn your own recipe\/cookbook. To install the RubyGem rmagick,\n\n include_recipe \"imagemagick::rmagick\"\n \nWhich will install imagemagick, as well as the development libraries for imagemagick (so rmagick can be built).\n\n= LICENSE:\n\nAuthor:: Joshua Timberman ()\n\nCopyright:: 2009, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/imagemagick/metadata.rb b/imagemagick/metadata.rb new file mode 100644 index 0000000..7b508d5 --- /dev/null +++ b/imagemagick/metadata.rb @@ -0,0 +1,10 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs/Configures imagemagick" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.1" + +%w{fedora centos rhel ubuntu debian}.each do |os| + supports os +end diff --git a/imagemagick/recipes/default.rb b/imagemagick/recipes/default.rb new file mode 100644 index 0000000..921a904 --- /dev/null +++ b/imagemagick/recipes/default.rb @@ -0,0 +1,25 @@ +# +# Cookbook Name:: imagemagick +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node[:platform] +when "redhat", "centos", "fedora" + package "ImageMagick" +when "debian", "ubuntu" + package "imagemagick" +end diff --git a/imagemagick/recipes/rmagick.rb b/imagemagick/recipes/rmagick.rb new file mode 100644 index 0000000..f951107 --- /dev/null +++ b/imagemagick/recipes/rmagick.rb @@ -0,0 +1,28 @@ +# +# Cookbook Name:: imagemagick +# Recipe:: rmagick +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +include_recipe "imagemagick" + +case node[:platform] +when "redhat", "centos", "fedora" + package "ImageMagick-devel" +when "debian", "ubuntu" + package "libmagickwand-dev" +end + +gem_package "rmagick" diff --git a/instiki/README.rdoc b/instiki/README.rdoc new file mode 100644 index 0000000..3170119 --- /dev/null +++ b/instiki/README.rdoc @@ -0,0 +1,33 @@ += DESCRIPTION: + +Installs instiki, a Ruby on Rails wiki server under passenger+Apache2. + += REQUIREMENTS: + +Opscode cookbooks: + +* apache2 +* passenger_apache2 +* sqlite +* rails + += USAGE: + +Simply apply the 'instiki' recipe. This installs version 0.17 in /srv/instiki/instiki-0.17. A later version of this cookbook might support setting the version and a different installation path, but for now these are hardcoded. + += LICENSE and AUTHOR: + +Author:: Joshua Timberman + +Copyright 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and diff --git a/instiki/metadata.rb b/instiki/metadata.rb new file mode 100644 index 0000000..0320601 --- /dev/null +++ b/instiki/metadata.rb @@ -0,0 +1,10 @@ +maintainer "Opscode" +maintainer_email "joshua@opscode.com" +license "Apache 2.0" +description "Installs instiki, a Ruby on Rails wiki server under passenger+Apache2." +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.1" + +%w{ apache2 passenger_apache2 sqlite rails }.each do |cb| + depends cb +end diff --git a/instiki/recipes/default.rb b/instiki/recipes/default.rb new file mode 100644 index 0000000..4d25348 --- /dev/null +++ b/instiki/recipes/default.rb @@ -0,0 +1,50 @@ +# +# Cookbook Name:: instiki +# Recipe:: default +# +# Copyright 2009, Opscode +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "sqlite" +include_recipe "rails" +include_recipe "apache2" +include_recipe "apache2::mod_rewrite" +include_recipe "passenger_apache2" +include_recipe "passenger_apache2::mod_rails" + +remote_file "/tmp/instiki-0.17.tar.gz" do + source "http://rubyforge.org/frs/download.php/59127/instiki-0.17.tgz" + mode 0644 + owner "root" + group "root" + not_if { ::FileTest.exists?("/tmp/instiki-0.17.tar.gz") } +end + +directory "/srv/instiki" do + owner node[:apache][:user] +end + +execute "tar zxf /tmp/instiki-0.17.tar.gz -C /srv/instiki" do + user node[:apache][:user] + creates "/srv/instiki/instiki-0.17/instiki" +end + +web_app "instiki" do + docroot "/srv/instiki/instiki-0.17/public" + template "instiki.conf.erb" + server_name "wiki.#{node[:domain]}" + server_aliases [ "wiki", "instiki", node[:hostname] ] + rails_env "production" +end diff --git a/instiki/templates/default/instiki.conf.erb b/instiki/templates/default/instiki.conf.erb new file mode 100644 index 0000000..2602e6a --- /dev/null +++ b/instiki/templates/default/instiki.conf.erb @@ -0,0 +1,20 @@ + + ServerName <%= @params[:server_name] %> + ServerAlias <% @params[:server_aliases].each do |a| %><%= "#{a}" %> <% end %> + DocumentRoot <%= @params[:docroot] %> + + RailsBaseURI / + RailsEnv <%= @params[:rails_env] %> + + > + Options FollowSymLinks + AllowOverride None + Order allow,deny + Allow from all + + + LogLevel info + ErrorLog <%= @node[:apache][:log_dir] %>/<%= @params[:name] %>-error.log + CustomLog <%= @node[:apache][:log_dir] %>/<%= @params[:name] %>-access.log combined + + diff --git a/integrity/attributes/integrity.rb b/integrity/attributes/integrity.rb new file mode 100755 index 0000000..1d7386d --- /dev/null +++ b/integrity/attributes/integrity.rb @@ -0,0 +1,11 @@ +integrity Mash.new unless attribute?(:integrity) +integrity[:server_name] = "integrity.37signals.com" unless integrity.has_key?(:server_name) +integrity[:url] = "http://#{integrity[:server_name]}" unless integrity.has_key?(:url) +integrity[:path] = "/u/apps/integrity" unless integrity.has_key?(:path) +integrity[:db_uri] = "sqlite3://#{integrity[:path]}/integrity.db" unless integrity.has_key?(:db_uri) +integrity[:export_dir] = "#{integrity[:path]}/builds" unless integrity.has_key?(:export_dir) +integrity[:log_file] = "#{integrity[:path]}/log/integrity.log" unless integrity.has_key?(:log_file) +integrity[:basic_auth] = "true" unless integrity.has_key?(:basic_auth) +integrity[:admin_username] = "admin" unless integrity.has_key?(:admin_username) +integrity[:admin_password] = "12345678" unless integrity.has_key?(:admin_password) +integrity[:hash_admin_password] = "true" unless integrity.has_key?(:hash_admin_password) diff --git a/integrity/files/default/integrity_build.rb b/integrity/files/default/integrity_build.rb new file mode 100755 index 0000000..9ede31a --- /dev/null +++ b/integrity/files/default/integrity_build.rb @@ -0,0 +1,22 @@ +#!/usr/bin/env ruby + +# Integrity periodic build script. +# Original: http://gist.github.com/88432 + +ENV["RAILS_ENV"] = "test" +ENV["PATH"] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + +require 'rubygems' +require 'integrity' +require 'notifier/campfire' + +Integrity::Notifier.register(Integrity::Notifier::Campfire) + +Integrity.new(File.dirname(__FILE__) + "/config.yml") + +Integrity::Project.all.each do |project| + last_commit = project.commits.last + build = last_commit ? project.send(:head_of_remote_repo) != last_commit.identifier : false + project.build if build || project.commits.last.nil? +end + diff --git a/integrity/metadata.json b/integrity/metadata.json new file mode 100755 index 0000000..64ea7be --- /dev/null +++ b/integrity/metadata.json @@ -0,0 +1,40 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "integrity": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + "passenger": [ + + ] + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures integrity", + "version": "0.1.0", + "name": "integrity", + "providing": { + "integrity": [ + + ] + } +} \ No newline at end of file diff --git a/integrity/metadata.rb b/integrity/metadata.rb new file mode 100755 index 0000000..b154a01 --- /dev/null +++ b/integrity/metadata.rb @@ -0,0 +1,5 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures integrity" +version "0.1" +depends "passenger" \ No newline at end of file diff --git a/integrity/recipes/default.rb b/integrity/recipes/default.rb new file mode 100755 index 0000000..f3693f1 --- /dev/null +++ b/integrity/recipes/default.rb @@ -0,0 +1,111 @@ +require_recipe "passenger" + +gem_package "integrity" do + source "http://localgems" +end + +gem_package "integrity-campfire" do + source "http://localgems" +end + +gem_package "fiveruns-dash-rails" do + source "http://gems.github.com" +end + +gem_package "do_sqlite3" +gem_package "do_mysql" +gem_package "mocha" +gem_package "rcov" +gem_package "ruby-debug" +gem_package "quietbacktrace" +gem_package "tinder" + +if node[:integrity][:projects] + node[:integrity][:projects].each do |app| + if node[:applications][app][:gems] + node[:applications][app][:gems].each do |g| + if g.is_a? Array + gem_package g.first do + version g.last + end + else + gem_package g + end + end + end + + if node[:applications][app][:packages] + node[:applications][app][:packages].each do |package_name| + package package_name + end + end + + if node[:applications][app][:symlinks] + node[:applications][app][:symlinks].each do |target, source| + link target do + to source + end + end + end + end +end + +directory "/u/apps" do + owner "app" + group "app" + mode 0755 + recursive true +end + +execute "setup_integrity" do + command "integrity install #{node[:integrity][:path]} --passenger" + user "app" + group "app" + not_if "test -e /u/apps/integrity" +end + +template "#{node[:integrity][:path]}/config.ru" do + source "config.ru.erb" + owner "app" + group "app" + mode 0644 +end + +template "#{node[:integrity][:path]}/config.yml" do + source "config.yml.erb" + owner "app" + group "app" + mode 0644 +end + +template "#{node[:integrity][:path]}/vhost.conf" do + source "vhost.conf.erb" + owner "app" + group "app" + mode 0644 +end + +remote_file "#{node[:integrity][:path]}/integrity_build.rb" do + source "integrity_build.rb" + owner "app" + group "app" + mode 0700 +end + +cron "integrity_build" do + user "app" + minute "*/10" + command "/usr/local/bin/ruby #{node[:integrity][:path]}/integrity_build.rb" + only_if { File.exist?(File.join(node[:integrity][:path], "integrity_build.rb")) } +end + +apache_site "integrity" do + config_path "#{node[:integrity][:path]}/vhost.conf" + not_if { File.exists?("/etc/apache2/sites-enabled/integrity") } +end + +logrotate "integrity" do + files "#{node[:integrity][:path]}/log/*.log" + frequency "weekly" + restart_command "/etc/init.d/apache2 reload > /dev/null" +end diff --git a/integrity/templates/default/config.ru.erb b/integrity/templates/default/config.ru.erb new file mode 100755 index 0000000..7b513ac --- /dev/null +++ b/integrity/templates/default/config.ru.erb @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby +require "rubygems" +require "integrity" +require 'notifier/campfire' + +# Load configuration and initialize Integrity +Integrity.new(File.dirname(__FILE__) + "/config.yml") +Integrity::Notifier.register(Integrity::Notifier::Campfire) + +# You probably don't want to edit anything below +Integrity::App.set :environment, ENV["RACK_ENV"] || :production +Integrity::App.set :port, 8910 + +run Integrity::App diff --git a/integrity/templates/default/config.yml.erb b/integrity/templates/default/config.yml.erb new file mode 100755 index 0000000..73a2535 --- /dev/null +++ b/integrity/templates/default/config.yml.erb @@ -0,0 +1,41 @@ +# Domain where integrity will be running from. This is used to have +# nice URLs in your notifications. +# For example: +# http://builder.integrityapp.com +:base_uri: <%= @node[:integrity][:url] %> + +# This should be a complete connection string to your database. +# +# Examples: +# * `mysql://user:password@localhost/integrity` +# * `postgres://user:password@localhost/integrity` +# * `sqlite3:///home/integrity/db/integrity.sqlite` +# +# Note: +# * The appropriate data_objects adapter must be installed (`do_mysql`, etc) +# * You must create the `integrity` database on localhost, of course. +:database_uri: <%= @node[:integrity][:db_uri] %> + +# This is where your project's code will be checked out to. Make sure it's +# writable by the user that runs Integrity. +:export_directory: <%= @node[:integrity][:export_dir] %> + +# Path to the integrity log file +:log: <%= @node[:integrity][:log_file] %> + +# Enable or disable HTTP authentication for the app. BE AWARE that if you +# disable this anyone can delete and alter projects, so do it only if your +# app is running in a controlled environment (ie, behind your company's +# firewall.) +:use_basic_auth: <%= @node[:integrity][:basic_auth] %> + +# When `use_basic_auth` is true, the admin's username for HTTP authentication. +:admin_username: <%= @node[:integrity][:admin_username] %> + +# When `use_basic_auth` is true, the admin's password. Usually saved as a +# SHA1 hash. See the next option. +:admin_password: <%= @node[:integrity][:admin_password] %> + +# If this is true, then whenever we authenticate the admin user, will hash +# it using SHA1. If not, we'll assume the provided password is in plain text. +:hash_admin_password: <%= @node[:integrity][:hash_admin_password] %> diff --git a/integrity/templates/default/vhost.conf.erb b/integrity/templates/default/vhost.conf.erb new file mode 100755 index 0000000..6511beb --- /dev/null +++ b/integrity/templates/default/vhost.conf.erb @@ -0,0 +1,12 @@ + + ServerName <%= @node[:integrity][:server_name] %> + DocumentRoot <%= @node[:integrity][:path] %>/public + ErrorLog <%= @node[:integrity][:path] %>/log/error.log + CustomLog <%= @node[:integrity][:path] %>/log/access.log combined + + /public"> + AllowOverride None + Order allow,deny + Allow from all + + \ No newline at end of file diff --git a/iptables/definitions/iptables_rule.rb b/iptables/definitions/iptables_rule.rb new file mode 100644 index 0000000..5ad59d2 --- /dev/null +++ b/iptables/definitions/iptables_rule.rb @@ -0,0 +1,34 @@ +# +# Cookbook Name:: iptables +# Definition:: iptables_rule +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :iptables_rule, :enable => true, :source => nil, :variables => {} do + template_source = params[:source] ? params[:source] : "#{params[:name]}.erb" + + template "/etc/iptables.d/#{params[:name]}" do + source template_source + mode 0644 + variables params[:variables] + notifies :run, resources(:execute => "rebuild-iptables") + if params[:enable] + action :create + else + action :delete + end + end +end diff --git a/iptables/files/default/rebuild-iptables b/iptables/files/default/rebuild-iptables new file mode 100644 index 0000000..fda9873 --- /dev/null +++ b/iptables/files/default/rebuild-iptables @@ -0,0 +1,282 @@ +#!/usr/bin/perl -w +our $ID = q$Id: rebuild-iptables 344 2006-10-04 02:48:30Z digant $; + +# +# rebuild-iptables -- Construct an iptables rules file from fragments. +# +# Written by Russ Allbery +# Adapted by Digant C Kasundra +# Copyright 2005, 2006 Board of Trustees, Leland Stanford Jr. University +# +# Constructs an iptables rules file from the prefix, standard, and suffix +# files in the iptables configuration area, adding any additional modules +# specified in the command line, and prints the resulting iptables rules to +# standard output (suitable for saving into /var/lib/iptables or some other +# appropriate location on the system). + +############################################################################## +# Modules and declarations +############################################################################## + +require 5.006; +use strict; + +use Getopt::Long qw(GetOptions); + +# Path to the iptables template area. +our $TEMPLATE = '/etc/iptables.d'; + +############################################################################## +# Installation +############################################################################## + +# Return the prefix +sub prefix { + my $data; + ( $data = <<'END_OF_PREFIX' ) =~ s/^\s+//gm; + *filter + :INPUT ACCEPT + :FORWARD ACCEPT + :OUTPUT ACCEPT + # :FWR - + # -A INPUT -j FWR + # -A FWR -i lo -j ACCEPT +END_OF_PREFIX + + return $data; +} + +# Return the suffix +sub suffix { + my $data; + ( $data = <<'END_OF_SUFFIX' ) =~ s/^\s+//gm; + # Rejects all remaining connections with port-unreachable errors. + + # -A FWR -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j REJECT --reject-with icmp-port-unreachable + # -A FWR -p udp -j REJECT --reject-with icmp-port-unreachable + COMMIT +END_OF_SUFFIX + + return $data; +} + +sub snat { + my $data = ""; + if ( -f "/etc/iptables.snat" ) { + open( SNAT, "<", "/etc/iptables.snat" ) + or die "$0: cannot open /etc/iptables.snat: $!\n"; + while () { + $data = $data . $_; + } + close(SNAT); + } + return $data; +} + +# Read in a file, processing includes as required. Returns the contents of +# the file as an array. +sub read_iptables { + my ($file) = @_; + my @data; + $file = $TEMPLATE . '/' . $file unless $file =~ m%^\.?/%; + local *MODULE; + open( MODULE, '<', $file ) or die "$0: cannot open $file: $!\n"; + local $_; + while () { + if (/^\s*include\s+(\S+)$/) { + my $included = $1; + $included = $TEMPLATE . '/' . $included + unless $included =~ m%^\.?/%; + if ( $file eq $included ) { + die "$0: include loop in $file, line $.\n"; + } + push( @data, "\n" ); + push( @data, read_iptables($included) ); + push( @data, "\n" ); + } elsif (/^\s*include\s/) { + die "$0: malformed include line in $file, line $.\n"; + } else { + push( @data, $_ ); + } + } + close MODULE; + return @data; +} + +# Write a file carefully. +sub write_iptables { + my ( $file, @data ) = @_; + open( NEW, "> $file.new" ) or die "$0: cannot create $file.new: $!\n"; + print NEW @data or die "$0: cannot write to $file.new: $!\n"; + close NEW or die "$0: cannot flush $file.new: $!\n"; + rename( "$file.new", $file ) + or die "$0: cannot install new $file: $!\n"; +} + +# Install iptables on a Red Hat system. Takes the array containing the new +# iptables data. +sub install_redhat { + my (@data) = @_; + write_iptables( '/etc/sysconfig/iptables', @data ); + system( "/sbin/service", "iptables", "restart" ); +} + +# Install iptables on a Debian system. Take the array containing the new +# iptables data. +sub install_debian { + my (@data) = @_; + unless ( -d '/etc/iptables' ) { + mkdir( '/etc/iptables', 0755 ) + or die "$0: cannot mkdir /etc/iptables: $!\n"; + } + write_iptables( "/etc/iptables/general", @data ); + system("/sbin/iptables-restore < /etc/iptables/general"); +} + +############################################################################## +# Main routine +############################################################################## + +# Fix things up for error reporting. +$| = 1; +my $fullpath = $0; +$0 =~ s%.*/%%; + +# Parse command-line options. +my ( $help, $version ); +Getopt::Long::config( 'bundling', 'no_ignore_case' ); +GetOptions( + 'h|help' => \$help, + 'v|version' => \$version +) or exit 1; +if ($help) { + print "Feeding myself to perldoc, please wait....\n"; + exec( 'perldoc', '-t', $fullpath ); +} elsif ($version) { + my $version = join( ' ', ( split( ' ', $ID ) )[ 1 .. 3 ] ); + $version =~ s/,v\b//; + $version =~ s/(\S+)$/($1)/; + $version =~ tr%/%-%; + print $version, "\n"; + exit; +} +my @modules; + +if ( -d '/etc/iptables.d' ) { + @modules = ; +} + +# Concatenate everything together. +my @data; +push( @data, prefix() ); +push( @data, "\n" ); +for my $module (@modules) { + push( @data, read_iptables($module) ); + push( @data, "\n" ); +} +push( @data, suffix() ); +push( @data, snat() ); + +if ( -f '/etc/debian_version' ) { + install_debian(@data); +} elsif ( -f '/etc/redhat-release' ) { + install_redhat(@data); +} else { + die "$0: cannot figure out whether this is Red Hat or Debian\n"; +} + +exit 0; +__END__ + +############################################################################## +# Documentation +############################################################################## + +=head1 NAME + +rebuild-iptables - Construct an iptables rules file from fragments + +=head1 SYNOPSIS + +rebuild-iptables [B<-hv>] + +=head1 DESCRIPTION + +B constructs an iptables configuration file by concatenating +various modules found in F. The resulting iptables +configuration file is written to the appropriate file for either Red Hat or +Debian (determined automatically) and iptables is restarted. + +Each module is just a text file located in the directory mentioned above that +contains one or more iptables configuration lines (basically the arguments to +an B invocation), possibly including comments. + +Along with the modules in the directory specified, a standard prefix and suffix +is added. + +Normally, the contents of each module are read in verbatim, but a module may +also contain the directive: + + include + +on a separate line, where is the path to another module to include, +specified the same way as modules given on the command line (hence, either a +file name relative to F or an +absolute path). Such a line will be replaced with the contents of the named +file. Be careful when using this directive to not create loops; files +including themselves will be detected, but more complex loops will not and +will result in infinite output. + +=head1 OPTIONS + +=over 4 + +=item B<-h>, B<--help> + +Print out this documentation (which is done simply by feeding the script to +C). + +=item B<-v>, B<--version> + +Print out the version of B and exit. + +=back + +=head1 FILES + +=over 4 + +=item F + +The default module location. + +=item F + +If this file exists, the system is assumed to be a Debian system for +determining the installation location when B<-i> is used. + +=item F + +The install location of the generated configuration file on Debian. + +=item F + +If this file exists, the system is assumed to be a Red Hat system for +determining the installation location when B<-i> is used. + +=item F + +The install location of the generated configuration file on Red Hat. + +=back + +=head1 AUTHOR + +Russ Allbery +Digant C Kasundra + +=head1 SEE ALSO + +iptables(8) + +=cut diff --git a/iptables/metadata.json b/iptables/metadata.json new file mode 100644 index 0000000..b351efb --- /dev/null +++ b/iptables/metadata.json @@ -0,0 +1,49 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Sets up iptables to use a script to maintain rules", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "iptables": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "centos": [ + + ], + "debian": [ + + ], + "redhat": [ + + ] + }, + "version": "0.7.0", + "name": "iptables", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "iptables": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/iptables/metadata.rb b/iptables/metadata.rb new file mode 100644 index 0000000..9166b0d --- /dev/null +++ b/iptables/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Sets up iptables to use a script to maintain rules" +version "0.7" + +%w{ redhat centos debian ubuntu}.each do |os| + supports os +end diff --git a/iptables/recipes/default.rb b/iptables/recipes/default.rb new file mode 100644 index 0000000..477ac46 --- /dev/null +++ b/iptables/recipes/default.rb @@ -0,0 +1,36 @@ +# +# Cookbook Name:: iptables +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "iptables" + +execute "rebuild-iptables" do + command "/usr/sbin/rebuild-iptables" + action :nothing +end + +directory "/etc/iptables.d" do + action :create +end + +remote_file "/usr/sbin/rebuild-iptables" do + source "rebuild-iptables" + mode 0755 +end + +iptables_rule "restrictive" diff --git a/iptables/templates/default/restrictive.erb b/iptables/templates/default/restrictive.erb new file mode 100644 index 0000000..88fa22e --- /dev/null +++ b/iptables/templates/default/restrictive.erb @@ -0,0 +1,14 @@ +# Very restricted, only accepts 22, 80, 443 and loopback +-A INPUT -i lo -j ACCEPT +-A OUTPUT -o lo -j ACCEPT +-A OUTPUT -p udp -o eth0 --dport 53 --sport 1024:65535 -j ACCEPT +-A INPUT -p udp -i eth0 --sport 53 --dport 1024:65535 -j ACCEPT +-A OUTPUT -o eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT +-A INPUT -p tcp -i eth0 --dport 22 --sport 1024:65535 -m state --state NEW -j ACCEPT +-A INPUT -p tcp -i eth0 --dport 80 --sport 1024:65535 -m state --state NEW -j ACCEPT +-A OUTPUT -j ACCEPT -m state --state NEW,ESTABLISHED,RELATED -o eth0 -p tcp -m multiport --dport 80,443 -m multiport --sport 1024:65535 +-A INPUT -j ACCEPT -m state --state ESTABLISHED,RELATED -i eth0 -p tcp +-A INPUT -j DROP +-A OUTPUT -j DROP +-A FORWARD -j DROP + diff --git a/java/README.rdoc b/java/README.rdoc new file mode 100644 index 0000000..8e9d148 --- /dev/null +++ b/java/README.rdoc @@ -0,0 +1,29 @@ += DESCRIPTION: + +Installs Java and Ant. + += REQUIREMENTS: + +Platform: Ubuntu, Debian, Red Hat, CentOS, Fedora. + +Enable the 'multiverse' repository on Ubuntu, 'non-free' on Debian or EPEL on RH/Cent/Fedora to get the Java package, or edit the default recipe to point at the Java package for your platform. + += USAGE: + +Simply include the recipe where you want Java installed. Note the respository requirement above to get the right package. On Debian and Ubuntu systems, the recipe will preseed the package and update java alternaties. + += LICENSE and AUTHOR: + +Copyright 2008-2010, Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/java/files/default/java.seed b/java/files/default/java.seed new file mode 100644 index 0000000..bf44093 --- /dev/null +++ b/java/files/default/java.seed @@ -0,0 +1,11 @@ +sun-java6-bin shared/accepted-sun-dlj-v1-1 boolean true +sun-java6-jdk shared/accepted-sun-dlj-v1-1 boolean true +sun-java6-jre shared/accepted-sun-dlj-v1-1 boolean true +sun-java6-jre sun-java6-jre/stopthread boolean true +sun-java6-jre sun-java6-jre/jcepolicy note +sun-java6-bin shared/error-sun-dlj-v1-1 error +sun-java6-jdk shared/error-sun-dlj-v1-1 error +sun-java6-jre shared/error-sun-dlj-v1-1 error +sun-java6-bin shared/present-sun-dlj-v1-1 note +sun-java6-jdk shared/present-sun-dlj-v1-1 note +sun-java6-jre shared/present-sun-dlj-v1-1 note \ No newline at end of file diff --git a/java/metadata.json b/java/metadata.json new file mode 100644 index 0000000..0321b3b --- /dev/null +++ b/java/metadata.json @@ -0,0 +1,43 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs java", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "java": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "java", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "java": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/java/metadata.rb b/java/metadata.rb new file mode 100644 index 0000000..6e2ccd0 --- /dev/null +++ b/java/metadata.rb @@ -0,0 +1,10 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs java" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.9" + +%w{ debian ubuntu }.each do |os| + supports os +end diff --git a/java/recipes/default.rb b/java/recipes/default.rb new file mode 100644 index 0000000..2b47eae --- /dev/null +++ b/java/recipes/default.rb @@ -0,0 +1,45 @@ +# +# Cookbook Name:: java +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +java_pkg = value_for_platform( + [ "ubuntu", "debian" ] => { + "default" => "sun-java6-jdk" + }, + [ "redhat", "centos", "fedora" ] => { + "default" => "java-1.6.0-openjdk" + }, + "default" => "sun-java6-jdk" +) + +execute "update-java-alternatives" do + command "update-java-alternatives -s java-6-sun --jre" + only_if do platform?("ubuntu", "debian") end + ignore_failure true + action :nothing +end + +package java_pkg do + action :install + if platform?("ubuntu", "debian") + response_file "java.seed" + notifies :run, resources(:execute => "update-java-alternatives"), :immediately + end +end + +package "ant" diff --git a/jira/attributes/jira.rb b/jira/attributes/jira.rb new file mode 100644 index 0000000..7e86b26 --- /dev/null +++ b/jira/attributes/jira.rb @@ -0,0 +1,29 @@ +# +# Cookbook Name:: jira +# Attributes:: jira +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set_unless[:jira][:virtual_host_name] = "jira.#{domain}" +set_unless[:jira][:virtual_host_alias] = "jira.#{domain}" +# type-version-standalone +set_unless[:jira][:version] = "enterprise-3.13.1" +set_unless[:jira][:install_path] = "/srv/jira" +set_unless[:jira][:run_user] = "www-data" +set_unless[:jira][:database] = "mysql" +set_unless[:jira][:database_host] = "localhost" +set_unless[:jira][:database_user] = "jira" +set_unless[:jira][:database_password] = "change_me" diff --git a/jira/files/default/catalina.sh b/jira/files/default/catalina.sh new file mode 100644 index 0000000..c8ff1e6 --- /dev/null +++ b/jira/files/default/catalina.sh @@ -0,0 +1,323 @@ +#!/bin/sh +# ----------------------------------------------------------------------------- +# Start/Stop Script for the CATALINA Server +# +# Environment Variable Prequisites +# +# CATALINA_HOME May point at your Catalina "build" directory. +# +# CATALINA_BASE (Optional) Base directory for resolving dynamic portions +# of a Catalina installation. If not present, resolves to +# the same directory that CATALINA_HOME points to. +# +# CATALINA_OPTS (Optional) Java runtime options used when the "start", +# "stop", or "run" command is executed. +# +# CATALINA_TMPDIR (Optional) Directory path location of temporary directory +# the JVM should use (java.io.tmpdir). Defaults to +# $CATALINA_BASE/temp. +# +# JAVA_HOME Must point at your Java Development Kit installation. +# Required to run the with the "debug" or "javac" argument. +# +# JRE_HOME Must point at your Java Development Kit installation. +# Defaults to JAVA_HOME if empty. +# +# JAVA_OPTS (Optional) Java runtime options used when the "start", +# "stop", or "run" command is executed. +# +# JPDA_TRANSPORT (Optional) JPDA transport used when the "jpda start" +# command is executed. The default is "dt_socket". +# +# JPDA_ADDRESS (Optional) Java runtime options used when the "jpda start" +# command is executed. The default is 8000. +# +# JSSE_HOME (Optional) May point at your Java Secure Sockets Extension +# (JSSE) installation, whose JAR files will be added to the +# system class path used to start Tomcat. +# +# CATALINA_PID (Optional) Path of the file which should contains the pid +# of catalina startup java process, when start (fork) is used +# +# $Id: catalina.sh 394120 2006-04-14 15:25:07Z yoavs $ +# ----------------------------------------------------------------------------- + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false +os400=false +darwin=false +case "`uname`" in +CYGWIN*) cygwin=true;; +OS400*) os400=true;; +Darwin*) darwin=true;; +esac + +# resolve links - $0 may be a softlink +PRG="$0" + +while [ -h "$PRG" ]; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`/"$link" + fi +done + +# Get standard environment variables +PRGDIR=`dirname "$PRG"` + +# Only set CATALINA_HOME if not already set +[ -z "$CATALINA_HOME" ] && CATALINA_HOME=`cd "$PRGDIR/.." ; pwd` + +if [ -r "$CATALINA_HOME"/bin/setenv.sh ]; then + . "$CATALINA_HOME"/bin/setenv.sh +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$JRE_HOME" ] && JRE_HOME=`cygpath --unix "$JRE_HOME"` + [ -n "$CATALINA_HOME" ] && CATALINA_HOME=`cygpath --unix "$CATALINA_HOME"` + [ -n "$CATALINA_BASE" ] && CATALINA_BASE=`cygpath --unix "$CATALINA_BASE"` + [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix "$CLASSPATH"` + [ -n "$JSSE_HOME" ] && JSSE_HOME=`cygpath --absolute --unix "$JSSE_HOME"` +fi + +# For OS400 +if $os400; then + # Set job priority to standard for interactive (interactive - 6) by using + # the interactive priority - 6, the helper threads that respond to requests + # will be running at the same priority as interactive jobs. + COMMAND='chgjob job('$JOBNAME') runpty(6)' + system $COMMAND + + # Enable multi threading + export QIBM_MULTI_THREADED=Y +fi + +# Get standard Java environment variables +if $os400; then + # -r will Only work on the os400 if the files are: + # 1. owned by the user + # 2. owned by the PRIMARY group of the user + # this will not work if the user belongs in secondary groups + BASEDIR="$CATALINA_HOME" + . "$CATALINA_HOME"/bin/setclasspath.sh +else + if [ -r "$CATALINA_HOME"/bin/setclasspath.sh ]; then + BASEDIR="$CATALINA_HOME" + . "$CATALINA_HOME"/bin/setclasspath.sh + else + echo "Cannot find $CATALINA_HOME/bin/setclasspath.sh" + echo "This file is needed to run this program" + exit 1 + fi +fi + +# Add on extra jar files to CLASSPATH +if [ -n "$JSSE_HOME" ]; then + CLASSPATH="$CLASSPATH":"$JSSE_HOME"/lib/jcert.jar:"$JSSE_HOME"/lib/jnet.jar:"$JSSE_HOME"/lib/jsse.jar +fi +CLASSPATH="$CLASSPATH":"$CATALINA_HOME"/bin/bootstrap.jar:"$CATALINA_HOME"/bin/commons-logging-api.jar + +if [ -z "$CATALINA_BASE" ] ; then + CATALINA_BASE="$CATALINA_HOME" +fi + +if [ -z "$CATALINA_TMPDIR" ] ; then + # Define the java.io.tmpdir to use for Catalina + CATALINA_TMPDIR="$CATALINA_BASE"/temp +fi + +# Bugzilla 37848: When no TTY is available, don't output to console +have_tty=0 +if [ "`tty`" != "not a tty" ]; then + have_tty=1 +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + JAVA_HOME=`cygpath --absolute --windows "$JAVA_HOME"` + JRE_HOME=`cygpath --absolute --windows "$JRE_HOME"` + CATALINA_HOME=`cygpath --absolute --windows "$CATALINA_HOME"` + CATALINA_BASE=`cygpath --absolute --windows "$CATALINA_BASE"` + CATALINA_TMPDIR=`cygpath --absolute --windows "$CATALINA_TMPDIR"` + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$JSSE_HOME" ] && JSSE_HOME=`cygpath --absolute --windows "$JSSE_HOME"` + JAVA_ENDORSED_DIRS=`cygpath --path --windows "$JAVA_ENDORSED_DIRS"` +fi + +# Set juli LogManager if it is present +if [ -r "$CATALINA_HOME"/bin/tomcat-juli.jar ]; then + JAVA_OPTS="$JAVA_OPTS "-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager" "-Djava.util.logging.config.file="$CATALINA_BASE/conf/logging.properties" +fi + +# ----- Execute The Requested Command ----------------------------------------- + +# Bugzilla 37848: only output this if we have a TTY +if [ $have_tty -eq 1 ]; then + echo "Using CATALINA_BASE: $CATALINA_BASE" + echo "Using CATALINA_HOME: $CATALINA_HOME" + echo "Using CATALINA_TMPDIR: $CATALINA_TMPDIR" + if [ "$1" = "debug" -o "$1" = "javac" ] ; then + echo "Using JAVA_HOME: $JAVA_HOME" + else + echo "Using JRE_HOME: $JRE_HOME" + fi +fi + +if [ "$1" = "jpda" ] ; then + if [ -z "$JPDA_TRANSPORT" ]; then + JPDA_TRANSPORT="dt_socket" + fi + if [ -z "$JPDA_ADDRESS" ]; then + JPDA_ADDRESS="8000" + fi + if [ -z "$JPDA_OPTS" ]; then + JPDA_OPTS="-Xdebug -Xrunjdwp:transport=$JPDA_TRANSPORT,address=$JPDA_ADDRESS,server=y,suspend=n" + fi + CATALINA_OPTS="$CATALINA_OPTS $JPDA_OPTS" + shift +fi + +if [ "$1" = "debug" ] ; then + if $os400; then + echo "Debug command not available on OS400" + exit 1 + else + shift + if [ "$1" = "-security" ] ; then + echo "Using Security Manager" + shift + exec "$_RUNJDB" $JAVA_OPTS $CATALINA_OPTS \ + -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \ + -sourcepath "$CATALINA_HOME"/../../jakarta-tomcat-catalina/catalina/src/share \ + -Djava.security.manager \ + -Djava.security.policy=="$CATALINA_BASE"/conf/catalina.policy \ + -Dcatalina.base="$CATALINA_BASE" \ + -Dcatalina.home="$CATALINA_HOME" \ + -Djava.io.tmpdir="$CATALINA_TMPDIR" \ + org.apache.catalina.startup.Bootstrap "$@" start + else + exec "$_RUNJDB" $JAVA_OPTS $CATALINA_OPTS \ + -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \ + -sourcepath "$CATALINA_HOME"/../../jakarta-tomcat-catalina/catalina/src/share \ + -Dcatalina.base="$CATALINA_BASE" \ + -Dcatalina.home="$CATALINA_HOME" \ + -Djava.io.tmpdir="$CATALINA_TMPDIR" \ + org.apache.catalina.startup.Bootstrap "$@" start + fi + fi + +elif [ "$1" = "run" ]; then + + shift + if [ "$1" = "-security" ] ; then + echo "Using Security Manager" + shift + exec "$_RUNJAVA" $JAVA_OPTS $CATALINA_OPTS \ + -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \ + -Djava.security.manager \ + -Djava.security.policy=="$CATALINA_BASE"/conf/catalina.policy \ + -Dcatalina.base="$CATALINA_BASE" \ + -Dcatalina.home="$CATALINA_HOME" \ + -Djava.io.tmpdir="$CATALINA_TMPDIR" \ + org.apache.catalina.startup.Bootstrap "$@" start + else + exec "$_RUNJAVA" $JAVA_OPTS $CATALINA_OPTS \ + -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \ + -Dcatalina.base="$CATALINA_BASE" \ + -Dcatalina.home="$CATALINA_HOME" \ + -Djava.io.tmpdir="$CATALINA_TMPDIR" \ + org.apache.catalina.startup.Bootstrap "$@" start + fi + +elif [ "$1" = "start" ] ; then + + shift + touch "$CATALINA_BASE"/logs/catalina.out + if [ "$1" = "-security" ] ; then + echo "Using Security Manager" + shift + "$_RUNJAVA" $JAVA_OPTS $CATALINA_OPTS \ + -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \ + -Djava.security.manager \ + -Djava.security.policy=="$CATALINA_BASE"/conf/catalina.policy \ + -Dcatalina.base="$CATALINA_BASE" \ + -Dcatalina.home="$CATALINA_HOME" \ + -Djava.io.tmpdir="$CATALINA_TMPDIR" \ + org.apache.catalina.startup.Bootstrap "$@" start \ + >> "$CATALINA_BASE"/logs/catalina.out 2>&1 & + + if [ ! -z "$CATALINA_PID" ]; then + echo $! > $CATALINA_PID + fi + else + "$_RUNJAVA" $JAVA_OPTS $CATALINA_OPTS \ + -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \ + -Dcatalina.base="$CATALINA_BASE" \ + -Dcatalina.home="$CATALINA_HOME" \ + -Djava.io.tmpdir="$CATALINA_TMPDIR" \ + org.apache.catalina.startup.Bootstrap "$@" start \ + >> "$CATALINA_BASE"/logs/catalina.out 2>&1 & + + if [ ! -z "$CATALINA_PID" ]; then + echo $! > $CATALINA_PID + fi + fi + +elif [ "$1" = "stop" ] ; then + + shift + FORCE=0 + if [ "$1" = "-force" ]; then + shift + FORCE=1 + fi + + "$_RUNJAVA" $JAVA_OPTS $CATALINA_OPTS \ + -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \ + -Dcatalina.base="$CATALINA_BASE" \ + -Dcatalina.home="$CATALINA_HOME" \ + -Djava.io.tmpdir="$CATALINA_TMPDIR" \ + org.apache.catalina.startup.Bootstrap "$@" stop + + if [ $FORCE -eq 1 ]; then + if [ ! -z "$CATALINA_PID" ]; then + echo "Killing: `cat $CATALINA_PID`" + kill -9 `cat $CATALINA_PID` + else + echo "Kill failed: \$CATALINA_PID not set" + fi + fi + +elif [ "$1" = "version" ] ; then + + "$_RUNJAVA" \ + -classpath "$CATALINA_HOME/server/lib/catalina.jar" \ + org.apache.catalina.util.ServerInfo + +else + + echo "Usage: catalina.sh ( commands ... )" + echo "commands:" + if $os400; then + echo " debug Start Catalina in a debugger (not available on OS400)" + echo " debug -security Debug Catalina with a security manager (not available on OS400)" + else + echo " debug Start Catalina in a debugger" + echo " debug -security Debug Catalina with a security manager" + fi + echo " jpda start Start Catalina under JPDA debugger" + echo " run Start Catalina in the current window" + echo " run -security Start in the current window with security manager" + echo " start Start Catalina in a separate window" + echo " start -security Start in a separate window with security manager" + echo " stop Stop Catalina" + echo " stop -force Stop Catalina (followed by kill -KILL)" + echo " version What version of tomcat are you running?" + exit 1 + +fi diff --git a/jira/files/default/startup.sh b/jira/files/default/startup.sh new file mode 100644 index 0000000..d2f2819 --- /dev/null +++ b/jira/files/default/startup.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# ----------------------------------------------------------------------------- +# Start Script for the CATALINA Server +# +# $Id: startup.sh 385888 2006-03-14 21:04:40Z keith $ +# ----------------------------------------------------------------------------- + +# Better OS/400 detection: see Bugzilla 31132 +os400=false +darwin=false +case "`uname`" in +CYGWIN*) cygwin=true;; +OS400*) os400=true;; +Darwin*) darwin=true;; +esac + +# resolve links - $0 may be a softlink +PRG="$0" + +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`/"$link" + fi +done + +PRGDIR=`dirname "$PRG"` +EXECUTABLE=catalina.sh + +# Check that target executable exists +if $os400; then + # -x will Only work on the os400 if the files are: + # 1. owned by the user + # 2. owned by the PRIMARY group of the user + # this will not work if the user belongs in secondary groups + eval +else + if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then + echo "Cannot find $PRGDIR/$EXECUTABLE" + echo "This file is needed to run this program" + exit 1 + fi +fi + +exec "$PRGDIR"/"$EXECUTABLE" run "$@" diff --git a/jira/metadata.json b/jira/metadata.json new file mode 100644 index 0000000..60d52d5 --- /dev/null +++ b/jira/metadata.json @@ -0,0 +1,161 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs and configures jira", + "recommendations": { + "mysql": [ + + ] + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "jira": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "jira", + "conflicting": { + + }, + "attributes": { + "jira\/database_host": { + "default": "localhost", + "type": "string", + "multiple_values": false, + "description": "Hostname of the database server", + "display_name": "Jira Database Host", + "recipes": [ + + ], + "required": false + }, + "jira\/run_user": { + "default": "www-data", + "type": "string", + "multiple_values": false, + "description": "User the Jira instance should run as", + "display_name": "Jira Run User", + "recipes": [ + + ], + "required": false + }, + "jira\/version": { + "default": "enterprise-3.13.1", + "type": "string", + "multiple_values": false, + "description": "Version of Jira to download and install", + "display_name": "Jira Version", + "recipes": [ + + ], + "required": false + }, + "jira\/virtual_host_name": { + "default": "jira.domain", + "type": "string", + "multiple_values": false, + "description": "Apache ServerName for Jira virtual host", + "display_name": "Jira Virtual Hostname", + "recipes": [ + + ], + "required": false + }, + "jira\/install_path": { + "default": "\/srv\/jira", + "type": "string", + "multiple_values": false, + "description": "Filesystem location for Jira", + "display_name": "Jira Install Path", + "recipes": [ + + ], + "required": false + }, + "jira": { + "type": "hash", + "multiple_values": false, + "description": "Hash of Jira attributes", + "display_name": "Jira", + "recipes": [ + + ], + "required": false + }, + "jira\/database": { + "default": "mysql", + "type": "string", + "multiple_values": false, + "description": "Type of database Jira should use", + "display_name": "Jira Database", + "recipes": [ + + ], + "required": false + }, + "jira\/database_user": { + "default": "jira", + "type": "string", + "multiple_values": false, + "description": "Name of the database user for Jira", + "display_name": "Jira Database User", + "recipes": [ + + ], + "required": false + }, + "jira\/virtual_host_alias": { + "default": "jira", + "type": "string", + "multiple_values": false, + "description": "Apache ServerAlias for Jira virtual host", + "display_name": "Jira Virtual Hostalias", + "recipes": [ + + ], + "required": false + }, + "jira\/database_password": { + "default": "change_me", + "type": "string", + "multiple_values": false, + "description": "Password for the Jira Database User", + "display_name": "Jira Database Password", + "recipes": [ + + ], + "required": false + } + }, + "providing": { + "jira": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + "java": [ + + ], + "runit": [ + + ], + "apache2": [ + + ] + } +} \ No newline at end of file diff --git a/jira/metadata.rb b/jira/metadata.rb new file mode 100644 index 0000000..743f27d --- /dev/null +++ b/jira/metadata.rb @@ -0,0 +1,64 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures jira" +version "0.7" +recommends "mysql" + +%w{ ubuntu debian }.each do |os| + supports os +end + +%w{ runit java apache2 }.each do |cb| + depends cb +end + +attribute "jira", + :display_name => "Jira", + :description => "Hash of Jira attributes", + :type => "hash" + +attribute "jira/virtual_host_name", + :display_name => "Jira Virtual Hostname", + :description => "Apache ServerName for Jira virtual host", + :default => "jira.domain" + +attribute "jira/virtual_host_alias", + :display_name => "Jira Virtual Hostalias", + :description => "Apache ServerAlias for Jira virtual host", + :default => "jira" + +attribute "jira/version", + :display_name => "Jira Version", + :description => "Version of Jira to download and install", + :default => "enterprise-3.13.1" + +attribute "jira/install_path", + :display_name => "Jira Install Path", + :description => "Filesystem location for Jira", + :default => "/srv/jira" + +attribute "jira/run_user", + :display_name => "Jira Run User", + :description => "User the Jira instance should run as", + :default => "www-data" + +attribute "jira/database", + :display_name => "Jira Database", + :description => "Type of database Jira should use", + :default => "mysql" + +attribute "jira/database_host", + :display_name => "Jira Database Host", + :description => "Hostname of the database server", + :default => "localhost" + +attribute "jira/database_user", + :display_name => "Jira Database User", + :description => "Name of the database user for Jira", + :default => "jira" + +attribute "jira/database_password", + :display_name => "Jira Database Password", + :description => "Password for the Jira Database User", + :default => "change_me" diff --git a/jira/recipes/default.rb b/jira/recipes/default.rb new file mode 100644 index 0000000..13db573 --- /dev/null +++ b/jira/recipes/default.rb @@ -0,0 +1,99 @@ +# +# Cookbook Name:: jira +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# Manual Steps! +# +# MySQL: +# +# create database jiradb character set utf8; +# grant all privileges on jiradb.* to '$jira_user'@'localhost' identified by '$jira_password'; +# flush privileges; + +include_recipe "runit" +include_recipe "java" +include_recipe "apache2" +include_recipe "apache2::mod_rewrite" +include_recipe "apache2::mod_proxy" +include_recipe "apache2::mod_proxy_http" +include_recipe "apache2::mod_ssl" + +unless FileTest.exists?(node[:jira][:install_path]) + remote_file "jira" do + path "/tmp/jira.tar.gz" + source "http://www.atlassian.com/software/jira/downloads/binary/atlassian-jira-#{node[:jira][:version]}-standalone.tar.gz" + end + + bash "untar-jira" do + code "(cd /tmp; tar zxvf /tmp/jira.tar.gz)" + end + + bash "install-jira" do + code "mv /tmp/atlassian-jira-#{node[:jira][:version]}-standalone #{node[:jira][:install_path]}" + end + + if node[:jira][:database] == "mysql" + remote_file "mysql-connector" do + path "/tmp/mysql-connector.tar.gz" + source "http://downloads.mysql.com/archives/mysql-connector-java-5.1/mysql-connector-java-5.1.6.tar.gz" + end + + bash "untar-mysql-connector" do + code "(cd /tmp; tar zxvf /tmp/mysql-connector.tar.gz)" + end + + bash "install-mysql-connector" do + code "cp /tmp/mysql-connector-java-5.1.6/mysql-connector-java-5.1.6-bin.jar #{node[:jira][:install_path]}/common/lib" + end + end +end + +directory "#{node[:jira][:install_path]}" do + recursive true + owner "www-data" +end + +remote_file "#{node[:jira][:install_path]}/bin/startup.sh" do + source "startup.sh" + mode 0755 +end + +remote_file "#{node[:jira][:install_path]}/bin/catalina.sh" do + source "catalina.sh" + mode 0755 +end + +template "#{node[:jira][:install_path]}/conf/server.xml" do + source "server.xml.erb" + mode 0755 +end + +template "#{node[:jira][:install_path]}/atlassian-jira/WEB-INF/classes/entityengine.xml" do + source "entityengine.xml.erb" + mode 0755 +end + +runit_service "jira" + +template "#{node[:apache][:dir]}/sites-available/jira.conf" do + source "apache.conf.erb" + mode 0644 +end + +apache_site "jira.conf" diff --git a/jira/templates/default/apache.conf.erb b/jira/templates/default/apache.conf.erb new file mode 100644 index 0000000..1f472f4 --- /dev/null +++ b/jira/templates/default/apache.conf.erb @@ -0,0 +1,32 @@ + + DocumentRoot <%= @node[:jira][:install_path] %> + ServerAdmin ops@<%= @node[:domain] %> + <% if @node[:jira][:virtual_host_name] -%> + ServerName <%= @node[:jira][:virtual_host_name] %> + <% end -%> + <% if @node[:jira][:virtual_host_alias] -%> + <% va_list = @node[:jira][:virtual_host_alias].kind_of?(Array) ? @node[:jira][:virtual_host_alias] : [ @node[:jira][:virtual_host_alias] ] -%> + <% va_list.each do |va| -%> + ServerAlias <%= va %> + <% end -%> + <% end -%> + + ErrorLog <%= @node[:apache][:log_dir] %>/jira-error.log + TransferLog <%= @node[:apache][:log_dir] %>/jira-access.log + RewriteEngine On + RewriteLog <%= @node[:apache][:log_dir] %>/jira-rewrite.log + RewriteLogLevel 0 + + + Order deny,allow + Allow from all + + + ProxyRequests Off + ProxyPreserveHost On + + RewriteEngine On + + RewriteRule ^/(.*)$ http://127.0.0.1:8080/$1 [P,QSA,L] + ProxyPassReverse / http://127.0.0.1:8080 + diff --git a/jira/templates/default/entityengine.xml.erb b/jira/templates/default/entityengine.xml.erb new file mode 100644 index 0000000..3dd6635 --- /dev/null +++ b/jira/templates/default/entityengine.xml.erb @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jira/templates/default/server.xml.erb b/jira/templates/default/server.xml.erb new file mode 100644 index 0000000..b8704f7 --- /dev/null +++ b/jira/templates/default/server.xml.erb @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jira/templates/default/sv-jira-log-run.erb b/jira/templates/default/sv-jira-log-run.erb new file mode 100644 index 0000000..e51ae7e --- /dev/null +++ b/jira/templates/default/sv-jira-log-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +umask 0027 +exec svlogd -tt ./main diff --git a/jira/templates/default/sv-jira-run.erb b/jira/templates/default/sv-jira-run.erb new file mode 100644 index 0000000..42cbb5d --- /dev/null +++ b/jira/templates/default/sv-jira-run.erb @@ -0,0 +1,5 @@ +#!/bin/bash + +cd <%= @node[:jira][:install_path] %> +exec 2>&1 +exec chpst -u <%= @node[:jira][:run_user] %> env JAVA_HOME=/usr/lib/jvm/java-6-sun JAVA_OPTS="-Xms256m -Xmx256m" <%= @node[:jira][:install_path] %>/bin/startup.sh diff --git a/keepalived/README.rdoc b/keepalived/README.rdoc new file mode 100644 index 0000000..9226876 --- /dev/null +++ b/keepalived/README.rdoc @@ -0,0 +1,33 @@ += DESCRIPTION: + +Installs keepalived and pushes the configuration file out. + += REQUIREMENTS: + +== Platform: + +Tested on Ubuntu 8.10. + += ATTRIBUTES: + += USAGE: + +Modify the sample template to suit your environment and configuration requirements. + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () + +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/keepalived/metadata.json b/keepalived/metadata.json new file mode 100644 index 0000000..9caf4a3 --- /dev/null +++ b/keepalived/metadata.json @@ -0,0 +1,40 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs and configures keepalived", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "keepalived": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ] + }, + "version": "0.7.0", + "name": "keepalived", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "keepalived": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nInstalls keepalived and pushes the configuration file out.\n\n= REQUIREMENTS:\n\n== Platform:\n\nTested on Ubuntu 8.10.\n\n= ATTRIBUTES: \n\n= USAGE: \n\nModify the sample template to suit your environment and configuration requirements.\n\n= LICENSE and AUTHOR:\n \nAuthor:: Joshua Timberman ()\n\nCopyright:: 2009, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/keepalived/metadata.rb b/keepalived/metadata.rb new file mode 100644 index 0000000..ce47324 --- /dev/null +++ b/keepalived/metadata.rb @@ -0,0 +1,7 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures keepalived" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.7" +supports "ubuntu" diff --git a/keepalived/recipes/default.rb b/keepalived/recipes/default.rb new file mode 100644 index 0000000..e0404d4 --- /dev/null +++ b/keepalived/recipes/default.rb @@ -0,0 +1,35 @@ +# +# Cookbook Name:: keepalived +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "keepalived" do + action :install +end + +service "keepalived" do + supports :restart => true + action [:enable, :start] +end + +template "/etc/keepalived/keepalived.conf" do + source "keepalived.conf.erb" + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "keepalived") +end diff --git a/keepalived/templates/default/keepalived.conf.erb b/keepalived/templates/default/keepalived.conf.erb new file mode 100644 index 0000000..30d9d98 --- /dev/null +++ b/keepalived/templates/default/keepalived.conf.erb @@ -0,0 +1,48 @@ +! Sample Configuration File for keepalived +! Generated by Chef. + +global_defs { + notification_email { + acassen + } + notification_email_from root@<%= @node[:domain] %> + smtp_server 192.168.200.1 + smtp_connect_timeout 30 + router_id LVS_DEVEL +} + +vrrp_instance VI_1 { + interface eth0 + virtual_router_id 50 + nopreempt + priority 100 + advert_int 1 + virtual_ipaddress { + 192.168.200.11 + 192.168.200.12 + 192.168.200.13 + } +} + +virtual_server 10.10.10.2 1358 { + delay_loop 6 + lb_algo rr + lb_kind NAT + persistence_timeout 50 + protocol TCP + + sorry_server 192.168.200.200 1358 + + real_server 192.168.200.2 1358 { + weight 1 + HTTP_GET { + url { + path /canary + digest 640205b7b0fc66c1ea91c463fac6334d + } + connect_timeout 3 + nb_get_retry 3 + delay_before_retry 3 + } + } +} diff --git a/kickstart/README.rdoc b/kickstart/README.rdoc new file mode 100644 index 0000000..3aed317 --- /dev/null +++ b/kickstart/README.rdoc @@ -0,0 +1,51 @@ += DESCRIPTION: + +Creates an apache vhost and serves a very basic kickstart file. + += REQUIREMENTS: + +Red Hat Enterprise Linux, CentOS, or other platforms that support Kickstart :-). + +Opscode/cookbooks: + +* apache2 + += ATTRIBUTES: + +* kickstart[:rootpw] - set the root password. Use an encrypted string[1]. + +[1] a Ruby way to encrypt: +http://www.opensourcery.co.za/2009/05/01/quick-nix-shadow-passwords-with-ruby/ + += USAGE: + +You'll almost certainly want to edit ks.cfg.erb to suit your environment. As is, the provided template is used as a minimal fast install for creating virtual machines to run CentOS 5. Of particular note, the following should definitely be changed: + +* url - mirrors.kernel.org is usually fast for me, but maybe not for you. +* network - change the hostname. +* rootpw - this is an attribute, so you can change it by modifying the server. Use the encrypted password! + +Storage / disks should probably be customized, as well as firewall rules, SELinux policy, and the package list. + +The %post section will install Chef via Matthew Kent's RPMs, per the Chef Wiki instructions. + +To use the recipe on a system that will be the kickstart server, + + include_recipe "kickstart::server" + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/kickstart/attributes/kickstart.rb b/kickstart/attributes/kickstart.rb new file mode 100644 index 0000000..fa654db --- /dev/null +++ b/kickstart/attributes/kickstart.rb @@ -0,0 +1,2 @@ +set_unless[:kickstart][:rootpw] = nil +set_unless[:kickstart][:virtual_host_name] = "build.#{domain}" diff --git a/kickstart/metadata.json b/kickstart/metadata.json new file mode 100644 index 0000000..e6627a5 --- /dev/null +++ b/kickstart/metadata.json @@ -0,0 +1,49 @@ +{ + "maintainer": "Opscode", + "description": "Installs\/Configures kickstart", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "kickstart::server": "", + "kickstart": "" + }, + "suggestions": { + + }, + "platforms": { + "centos": [ + + ], + "redhat": [ + + ] + }, + "version": "0.2.0", + "name": "kickstart", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "kickstart::server": [ + + ], + "kickstart": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nCreates an apache vhost and serves a very basic kickstart file.\n\n= REQUIREMENTS:\n\nRed Hat Enterprise Linux, CentOS, or other platforms that support Kickstart :-).\n\nOpscode\/cookbooks:\n\n* apache2\n\n= ATTRIBUTES: \n\n* kickstart[:rootpw] - set the root password. Use an encrypted string[1].\n\n[1] a Ruby way to encrypt:\nhttp:\/\/www.opensourcery.co.za\/2009\/05\/01\/quick-nix-shadow-passwords-with-ruby\/\n\n= USAGE:\n\nYou'll almost certainly want to edit ks.cfg.erb to suit your environment. As is, the provided template is used as a minimal fast install for creating virtual machines to run CentOS 5. Of particular note, the following should definitely be changed:\n\n* url - mirrors.kernel.org is usually fast for me, but maybe not for you.\n* network - change the hostname.\n* rootpw - this is an attribute, so you can change it by modifying the server. Use the encrypted password!\n\nStorage \/ disks should probably be customized, as well as firewall rules, SELinux policy, and the package list.\n\nThe %post section will install Chef via Matthew Kent's RPMs, per the Chef Wiki instructions.\n\nTo use the recipe on a system that will be the kickstart server,\n\n include_recipe \"kickstart::server\"\n\n= LICENSE and AUTHOR:\n\nAuthor:: Joshua Timberman ()\nCopyright:: 2009, Opscode, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "replacing": { + + }, + "dependencies": { + "apache2": [ + + ] + } +} \ No newline at end of file diff --git a/kickstart/metadata.rb b/kickstart/metadata.rb new file mode 100644 index 0000000..462f871 --- /dev/null +++ b/kickstart/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode" +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs/Configures kickstart" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.2" +depends "apache2" +supports "redhat" +supports "centos" diff --git a/kickstart/recipes/default.rb b/kickstart/recipes/default.rb new file mode 100644 index 0000000..83a2140 --- /dev/null +++ b/kickstart/recipes/default.rb @@ -0,0 +1,18 @@ +# +# Cookbook Name:: kickstart +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/kickstart/recipes/server.rb b/kickstart/recipes/server.rb new file mode 100644 index 0000000..47aa583 --- /dev/null +++ b/kickstart/recipes/server.rb @@ -0,0 +1,52 @@ +# +# Cookbook Name:: kickstart +# Recipe:: server +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +include_recipe "apache2" + +directory "/srv/kickstart" do + owner "root" + group "root" + mode "0755" +end + +template "/srv/kickstart/ks.cfg" do + source "ks.cfg.erb" + mode "0644" + owner "root" + group "root" +end + +link "/srv/kickstart/index.html" do + to "/srv/kickstart/ks.cfg" +end + +template "#{node[:apache][:dir]}/sites-available/kickstart.conf" do + source "kickstart.conf.erb" + variables( + :virtual_host_name => node[:kickstart][:virtual_host_name], + :docroot => "/srv/kickstart" + ) + mode "0644" + owner "root" + group "root" +end + +apache_site "kickstart.conf" do + enable true +end + diff --git a/kickstart/templates/default/kickstart.conf.erb b/kickstart/templates/default/kickstart.conf.erb new file mode 100644 index 0000000..a8c8544 --- /dev/null +++ b/kickstart/templates/default/kickstart.conf.erb @@ -0,0 +1,27 @@ + + + DocumentRoot <%= @docroot %> + + ServerName <%= @virtual_host_name %> + ServerAlias <%= @virtual_host_name.split('.')[0] %> + + + Options FollowSymLinks + AllowOverride None + + + LogLevel info + ErrorLog <%= @node[:apache][:log_dir] %>/kickstart-error.log + CustomLog <%= @node[:apache][:log_dir] %>/kickstart-access.log combined + + > + Options Indexes FollowSymLinks MultiViews + AllowOverride All + Order allow,deny + allow from all + + + AddOutputFilterByType DEFLATE text/html text/plain text/xml + + + diff --git a/kickstart/templates/default/ks.cfg.erb b/kickstart/templates/default/ks.cfg.erb new file mode 100644 index 0000000..7a55bc3 --- /dev/null +++ b/kickstart/templates/default/ks.cfg.erb @@ -0,0 +1,250 @@ +# Kickstart file automatically generated by anaconda. + +install +text +reboot +url --url http://mirrors.kernel.org/centos/5.3/os/i386 +lang en_US.UTF-8 +keyboard us +network --device eth0 --bootproto dhcp --hostname centos5test.<%= @node[:domain] %> +rootpw --iscrypted <%= @node[:kickstart][:rootpw] %> +firewall --enabled --port=22:tcp +authconfig --enableshadow --enablemd5 +selinux --permissive +timezone --utc America/Los_Angeles +bootloader --location=mbr --driveorder=sda +# The following is the partition information you requested +# Note that any partitions you deleted are not expressed +# here so unless you clear all partitions first, this is +# not guaranteed to work +ignoredisk --drives=sdb,sdc +clearpart --drives=sda --all +part /boot --fstype ext3 --size=256 --asprimary --ondisk=sda +part pv.01 --grow --size=100 --asprimary --ondisk=sda +volgroup vg0 pv.01 --pesize=32768 +logvol / --fstype ext3 --name=rootlv --vgname=vg0 --size=5120 +logvol swap --fstype swap --name=swaplv --vgname=vg0 --size=512 + + +%packages --nobase +acl +anacron +apr +apr-util +attr +audit +audit-libs +audit-libs-python +authconfig +automake +basesystem +bash +bc +beecrypt +bind-libs +bind-utils +bzip2 +bzip2-libs +centos-release +centos-release-notes +checkpolicy +chkconfig +coreutils +cpio +cracklib +cracklib-dicts +cryptsetup-luks +crontabs +curl +cyrus-sasl +cyrus-sasl-lib +db4 +dbus +dbus-glib +device-mapper +diffutils +dmidecode +dmraid +e2fsprogs +e2fsprogs-libs +ed +eject +elfutils-libelf +ethtool +expat +file +filesystem +findutils +gawk +gcc +gdbm +gettext +git +glib2 +glibc +glibc-common +gnu-efi +gnupg +gpm +grep +groff +grub +gzip +hal +hdparm +hwdata +info +initscripts +iproute +iptables +iptstate +iputils +irqbalance +kbd +kernel +kernel-headers +kpartx +krb5-libs +kudzu +less +libacl +libaio +libattr +libcap +libgcc +libgcrypt +libgpg-error +libidn +libselinux +libselinux-python +libsemanage +libsepol +libstdc++ +libsysfs +libtermcap +libusb +libuser +libutempter +libvolume_id +libxml2 +libxml2-python +logrotate +logwatch +lsof +lvm2 +m2crypto +mailcap +mailx +make +MAKEDEV +man +man-pages +mcstrans +mgetty +microcode_ctl +mingetty +mkinitrd +mktemp +mlocate +module-init-tools +mutt +nash +nc +ncurses +neon +net-tools +newt +ntp +ntsysv +openldap +openssh +openssh-clients +openssh-server +openssl +pam +parted +passwd +pciutils +pcre +perl +perl-URI +pm-utils +policycoreutils +popt +postfix +postgresql-libs +prelink +procps +psacct +psmisc +python +python-elementtree +python-sqlite +python-urlgrabber +quota +readline +redhat-logos +rhpl +rootfiles +rpm +rpm-libs +rpm-python +rsync +ruby +ruby-devel +ruby-irb +ruby-libs +ruby-rdoc +screen +sed +selinux-policy +selinux-policy-targeted +setools +setserial +setup +shadow-utils +slang +sqlite +stunnel +subversion +sudo +sysfsutils +sysklogd +sysstat +SysVinit +tar +tcl +tcpdump +tcp_wrappers +telnet +termcap +time +tmpwatch +traceroute +tzdata +udev +unzip +usbutils +usermode +util-linux +vim-common +vim-enhanced +vim-minimal +vixie-cron +w3m +which +wireless-tools +yum +zip +zlib +-dhcpv6_client +-iptables-ipv6 +-Deployment_Guide-en-US +-system-config-securitylevel-tui + +%pre + +%post +rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-3.noarch.rpm +rpm -Uvh http://download.elff.bravenet.com/5/i386/elff-release-5-3.noarch.rpm +yum install -y rubygem-chef diff --git a/kvm/files/default/kvmtool b/kvm/files/default/kvmtool new file mode 100755 index 0000000..e9bd098 --- /dev/null +++ b/kvm/files/default/kvmtool @@ -0,0 +1,54 @@ +#!/usr/bin/env ruby +require 'rubygems' +require 'thor' +require 'yaml' +class KvmTool < Thor + + desc "list", "More useful list of kvm guests" + def list + @total_allocated_mem = 0 + list = `virsh list --all 2>&1` + lines = list.split("\n")[3..-1] + lines.each do |line| + num, name, state = line.split + info = `virsh dominfo #{name} 2>/dev/null | grep -v Connecting` + infoz = YAML.load(info) + cpu = infoz['CPU(s)'] + memory = infoz["Max memory"].split.first.to_i + @total_allocated_mem += memory + memory_in_gb = memory / 1024 / 1024 + puts "#{num} #{name} #{state} #{cpu} #{memory_in_gb}GB" + end + + puts "Total allocated memory: #{@total_allocated_mem / 1024} MB" + end + + desc "build", "Build a new KVM guest" + method_options :memory => :required, :cpu => :required, :hostname => :required, :ip_address => :required, :root_disk_size => :string + def build + root_disk_size = options[:root_disk_size] || "18G" + volume = options[:hostname].gsub("-", "_") + + puts `lvcreate -n kvm_#{volume} -L #{root_disk_size} VolGroupKVM 2>&1` + command = "vmbuilder kvm ubuntu --suite=intrepid --flavour=virtual --arch=amd64 --hostname=#{options[:hostname]} --mem=#{options[:memory]} --cpus=#{options[:cpu]} \ + --mirror=http://apt/archive-ubuntu/ubuntu --dest=/u/kvm/images/#{options[:hostname]} --tmp=/u/kvm/tmp --rootsize=16384 --swapsize=1024 \ + --bridge=br0 --ip=#{options[:ip_address]} --mask=255.255.252.0 --net=192.168.0.0 --bcast=192.168.3.255 --gw=192.168.1.1 --dns=192.168.2.63 \ + --addpkg=openssh-server --addpkg=acpid --addpkg=sysstat --addpkg=emacs22-nox --addpkg=vim --addpkg=git-core \ + --addpkg=mysql-client --addpkg=libmysqlclient15-dev --addpkg=build-essential --addpkg=syslog-ng --addpkg=ntp \ + --addpkg=curl --addpkg=wget --lang=en_US.UTF-8 \ + --templates=/usr/local/share/kvm/templates \ + --copy=/usr/local/share/kvm/files/manifest.txt \ + --execscript=/usr/local/share/kvm/scripts/postinstall.sh \ + --libvirt=qemu:///system --verbose --debug 2>&1" + puts system(command) + puts "Converting qcow2 image to LVM..." + puts `kvm-img convert /u/kvm/images/#{options[:hostname]}/disk0.qcow2 -O raw /u/kvm/images/#{options[:hostname]}/disk0.raw 2>&1` + puts `dd if=/u/kvm/images/#{options[:hostname]}/disk0.raw of=/dev/mapper/VolGroupKVM-kvm_#{volume} bs=1M 2>&1` + + puts "Cleaning up temporary files/directories..." + puts `rm -rvf /u/kvm/images/#{options[:hostname]}` + + end +end + +KvmTool.start \ No newline at end of file diff --git a/kvm/files/default/modules/2.6.27-11-server/kvm-amd.ko b/kvm/files/default/modules/2.6.27-11-server/kvm-amd.ko new file mode 100755 index 0000000000000000000000000000000000000000..cd5c4462121702bb78b05f3594468fff351e22e4 GIT binary patch literal 74568 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 literal 0 HcmV?d00001 diff --git a/kvm/files/default/modules/2.6.27-11-server/kvm.ko b/kvm/files/default/modules/2.6.27-11-server/kvm.ko new file mode 100755 index 0000000000000000000000000000000000000000..c7657173b9fff85943d52fa9ca2e7d5c7c80134f GIT binary patch literal 285317 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

dE7z`^y`#OZrW7RzY|A)wq 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�Y^W)Z^IjPiX3l8;CLSm2EGT{EJ@8wAcLqIo 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�~ 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 $1/etc/network/interfaces < $1/boot/grub/menu.lst.new <> $1/boot/grub/menu.lst.new +cat $1/boot/grub/menu.lst.new | sed "s/^\(kernel.*\)/\\1 console=tty0 console=ttyS0,115200n8/g" > $1/boot/grub/menu.lst diff --git a/kvm/files/default/templates/scripts/postinstall.sh b/kvm/files/default/templates/scripts/postinstall.sh new file mode 100755 index 0000000..578f3c5 --- /dev/null +++ b/kvm/files/default/templates/scripts/postinstall.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +echo "Enabling ttyS0 console..." +sh /usr/local/share/kvm/scripts/grub-console.sh $1 + +echo "Renaming sda[12] to vda[12] in fstab..." +sh /usr/local/share/kvm/scripts/rename-fstab-devices.sh $1 + +echo "Setup private 172.28 network on eth1..." +sh /usr/local/share/kvm/scripts/add-172-net.sh $1 diff --git a/kvm/files/default/templates/scripts/rename-fstab-devices.sh b/kvm/files/default/templates/scripts/rename-fstab-devices.sh new file mode 100755 index 0000000..8064490 --- /dev/null +++ b/kvm/files/default/templates/scripts/rename-fstab-devices.sh @@ -0,0 +1,5 @@ +#!/bin/sh -x + +cp $1/etc/fstab $1/etc/fstab.bak +cat $1/etc/fstab | sed "s/sda/vda/g" > $1/etc/fstab.new +mv $1/etc/fstab.new $1/etc/fstab diff --git a/kvm/files/default/templates/scripts/vmbuild.sh b/kvm/files/default/templates/scripts/vmbuild.sh new file mode 100755 index 0000000..4e76b34 --- /dev/null +++ b/kvm/files/default/templates/scripts/vmbuild.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +NAME=$1 +CPU=$2 +MEM=$3 +IP=$4 +VOLUME=`echo -n ${NAME} | sed "s/\-/_/g"` + +echo "Generating VM:" +echo " - Name: ${NAME}" +echo " - CPUs: ${CPU}" +echo " - RAM: ${MEM}" +echo " - IP: ${IP}" + +PATH=/usr/bin:/sbin:${PATH} + +lvcreate -n kvm_${VOLUME} -L 18G VolGroupKVM +vmbuilder kvm ubuntu --suite=intrepid --flavour=virtual --arch=amd64 --hostname=${NAME} --mem=${MEM} --cpus=${CPU} \ + --mirror=http://apt/archive-ubuntu/ubuntu --dest=/u/kvm/images/${NAME} --tmp=/u/kvm/tmp --rootsize=16384 --swapsize=1024 \ + --bridge=br0 --ip=${IP} --mask=255.255.252.0 --net=192.168.0.0 --bcast=192.168.3.255 --gw=192.168.1.1 --dns=192.168.2.63 \ + --addpkg=openssh-server --addpkg=acpid --addpkg=sysstat --addpkg=emacs22-nox --addpkg=vim --addpkg=git-core \ + --addpkg=mysql-client --addpkg=libmysqlclient15-dev --addpkg=build-essential --addpkg=syslog-ng --addpkg=ntp \ + --addpkg=curl --addpkg=wget --lang=en_US.UTF-8 \ + --templates=/usr/local/share/kvm/templates \ + --copy=/usr/local/share/kvm/files/manifest.txt \ + --execscript=/usr/local/share/kvm/scripts/postinstall.sh \ + --libvirt=qemu:///system --verbose --debug + +echo "Converting qcow2 image to LVM..." +kvm-img convert /u/kvm/images/${NAME}/disk0.qcow2 -O raw /u/kvm/images/${NAME}/disk0.raw +dd if=/u/kvm/images/${NAME}/disk0.raw of=/dev/mapper/VolGroupKVM-kvm_${VOLUME} bs=1M + +echo "Cleaning up temporary files/directories..." +rm -rvf /u/kvm/images/${NAME} diff --git a/kvm/files/default/templates/templates/libvirt/libvirtxml.tmpl b/kvm/files/default/templates/templates/libvirt/libvirtxml.tmpl new file mode 100755 index 0000000..d9feb1e --- /dev/null +++ b/kvm/files/default/templates/templates/libvirt/libvirtxml.tmpl @@ -0,0 +1,53 @@ + + $hostname + #echo $mem * 1024 # + $cpus + + hvm + + + + + + + destroy + restart + destroy + + /usr/bin/kvm +#if $bridge + + +#else + +#if $mac + +#end if + +#end if +#if $virtio_net + +#end if + + + + + + + +#for $disk in $disks + + + + +#end for + + + + + + + + + + diff --git a/kvm/metadata.json b/kvm/metadata.json new file mode 100755 index 0000000..33b1fc5 --- /dev/null +++ b/kvm/metadata.json @@ -0,0 +1,38 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "kvm": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures KVM virtualization environment", + "version": "0.1.0", + "name": "kvm", + "providing": { + "kvm": [ + + ] + } +} \ No newline at end of file diff --git a/kvm/metadata.rb b/kvm/metadata.rb new file mode 100755 index 0000000..d59a8cf --- /dev/null +++ b/kvm/metadata.rb @@ -0,0 +1,4 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures KVM virtualization environment" +version "0.1" diff --git a/kvm/recipes/default.rb b/kvm/recipes/default.rb new file mode 100755 index 0000000..efc0e22 --- /dev/null +++ b/kvm/recipes/default.rb @@ -0,0 +1,106 @@ +# custom vmbuilder debs +%W(kvm debootstrap kpartx python-cheetah devscripts python-libvirt).each { |pkg| package pkg } +%W(python-vm-builder_0.9-0ubuntu6_all.deb + ubuntu-vm-builder_0.9-0ubuntu6_all.deb).each do |pkg| + remote_file "/tmp/#{pkg}" do + source "http://dist/debs/intrepid/vmbuilder/#{pkg}" + not_if "test -e /usr/bin/#{pkg.split('_').first.sub('python-vm-', 'vm')}" + end + + package pkg do + provider Chef::Provider::Package::Dpkg + source "/tmp/#{pkg}" + only_if "test -e /tmp/#{pkg}" + end + + execute "remove_deb" do + command "rm -f /tmp/#{pkg}" + only_if "test -e /tmp/#{pkg}" + end +end + +%W(tmp images).each do |dir| + directory "/u/kvm/#{dir}" do + recursive true + end +end + +remote_directory "/usr/local/share/kvm" do + source "templates" + files_backup 2 + files_owner "root" + files_group "admin" + files_mode 0755 + owner "root" + group "admin" + mode 0750 +end + +remote_directory "/lib/modules/#{@node[:kernel][:release]}/extra" do + source "modules/#{@node[:kernel][:release]}" + files_backup 0 + files_owner "root" + files_group "root" + files_mode 0644 + owner "root" + group "root" + mode 0755 + ignore_failure true +end + +kvm_modules = Dir.glob("/lib/modules/#{@node[:kernel][:release]}/extra/kvm*.ko").map { |f| File.basename(f) } +%W(drivers/kvm arch/x86/kvm).each do |module_dir| + Dir.glob("/lib/modules/#{@node[:kernel][:release]}/kernel/#{module_dir}/*.ko").each do |old_module| + execute "backup old kvm module: #{old_module}" do + command "mv #{old_module} #{old_module}.orig" + only_if { kvm_modules.include?(File.basename(old_module)) && File.exist?(old_module) } + end + end +end + +bash "update kvm userspace" do + user "root" + cwd "/tmp" + + code <<-EOH + curl http://dist/misc/kvm-84.tar.bz2 | tar -C /usr/local -xjf - + mkdir /usr/bin/kvm-dist + mv /usr/bin/kvm* /usr/bin/kvm-dist/. + ln -sf /usr/local/kvm/bin/qemu-system-x86_64 /usr/bin/kvm + ln -sf /usr/local/kvm/bin/qemu-img /usr/bin/kvm-img + ln -sf /usr/local/kvm/bin/qemu-nbd /usr/bin/kvm-nbd + EOH + + not_if { File.directory?("/usr/local/kvm") } +end + +execute "modprobe" do + command "/sbin/depmod -a" + action :run + + only_if do + File.exist?("/lib/modules/#{@node[:kernel][:release]}/extra/kvm.ko") && + ( File.mtime("/lib/modules/#{@node[:kernel][:release]}/extra/kvm.ko") > + File.mtime("/lib/modules/#{@node[:kernel][:release]}/modules.dep") ) + end +end + +gem_package "thor" + +remote_file "/usr/local/bin/kvmtool" do + source "kvmtool" + mode 0755 +end + +service "libvirt-bin" do + supports :restart => false, :reload => true + action :enable +end + +template "/etc/libvirt/libvirtd.conf" do + source "libvirtd.conf.erb" + mode 0644 + owner "root" + group "root" + notifies :reload, resources(:service => "libvirt-bin") +end diff --git a/kvm/templates/default/libvirtd.conf.erb b/kvm/templates/default/libvirtd.conf.erb new file mode 100755 index 0000000..4345982 --- /dev/null +++ b/kvm/templates/default/libvirtd.conf.erb @@ -0,0 +1,227 @@ +# Master libvirt daemon configuration file +# +# For further information consult http://libvirt.org/format.html + + +################################################################# +# +# Network connectivity controls +# + +# Flag listening for secure TLS connections on the public TCP/IP port. +# NB, must pass the --listen flag to the libvirtd process for this to +# have any effect. +# +# It is necessary to setup a CA and issue server certificates before +# using this capability. +# +# This is enabled by default, uncomment this to disable it +#listen_tls = 0 + +# Listen for unencrypted TCP connections on the public TCP/IP port. +# NB, must pass the --listen flag to the libvirtd process for this to +# have any effect. +# +# Using the TCP socket requires SASL authentication by default. Only +# SASL mechanisms which support data encryption are allowed. This is +# DIGEST_MD5 and GSSAPI (Kerberos5) +# +# This is disabled by default, uncomment this to enable it. +#listen_tcp = 1 + + + +# Override the port for accepting secure TLS connections +# This can be a port number, or service name +# +#tls_port = "16514" + +# Override the port for accepting insecure TCP connections +# This can be a port number, or service name +# +#tcp_port = "16509" + + +# Override the default configuration which binds to all network +# interfaces. This can be a numeric IPv4/6 address, or hostname +# +# listen_addr = "192.168.0.1" + + +# Flag toggling mDNS advertizement of the libvirt service. +# +# Alternatively can disable for all services on a host by +# stopping the Avahi daemon +# +# This is enabled by default, uncomment this to disable it +#mdns_adv = 0 + +# Override the default mDNS advertizement name. This must be +# unique on the immediate broadcast network. +# +# The default is "Virtualization Host HOSTNAME", where HOSTNAME +# is subsituted for the short hostname of the machine (without domain) +# +#mdns_name = "Virtualization Host Joe Demo" + + +################################################################# +# +# UNIX socket access controls +# + +# Set the UNIX domain socket group ownership. This can be used to +# allow a 'trusted' set of users access to management capabilities +# without becoming root. +# +# This is restricted to 'root' by default. +unix_sock_group = "admin" + +# Set the UNIX socket permissions for the R/O socket. This is used +# for monitoring VM status only +# +# Default allows any user. If setting group ownership may want to +# restrict this to: +#unix_sock_ro_perms = "0777" + +# Set the UNIX socket permissions for the R/W socket. This is used +# for full management of VMs +# +# Default allows only root. If PolicyKit is enabled on the socket, +# the default will change to allow everyone (eg, 0777) +# +# If not using PolicyKit and setting group ownership for access +# control then you may want to relax this to: +unix_sock_rw_perms = "0770" + + + +################################################################# +# +# Authentication. +# +# - none: do not perform auth checks. If you can connect to the +# socket you are allowed. This is suitable if there are +# restrictions on connecting to the socket (eg, UNIX +# socket permissions), or if there is a lower layer in +# the network providing auth (eg, TLS/x509 certificates) +# +# - sasl: use SASL infrastructure. The actual auth scheme is then +# controlled from /etc/sasl2/libvirt.conf. For the TCP +# socket only GSSAPI & DIGEST-MD5 mechanisms will be used. +# For non-TCP or TLS sockets, any scheme is allowed. +# +# - polkit: use PolicyKit to authenticate. This is only suitable +# for use on the UNIX sockets. The default policy will +# require a user to supply their own password to gain +# full read/write access (aka sudo like), while anyone +# is allowed read/only access. +# +# Set an authentication scheme for UNIX read-only sockets +# By default socket permissions allow anyone to connect +# +# To restrict monitoring of domains you may wish to enable +# an authentication mechanism here +auth_unix_ro = "none" + +# Set an authentication scheme for UNIX read-write sockets +# By default socket permissions only allow root. If PolicyKit +# support was compiled into libvirt, the default will be to +# use 'polkit' auth. +# +# If the unix_sock_rw_perms are changed you may wish to enable +# an authentication mechanism here +auth_unix_rw = "none" + +# Change the authentication scheme for TCP sockets. +# +# If you don't enable SASL, then all TCP traffic is cleartext. +# Don't do this outside of a dev/test scenario. For real world +# use, always enable SASL and use the GSSAPI or DIGEST-MD5 +# mechanism in /etc/sasl2/libvirt.conf +#auth_tcp = "sasl" + +# Change the authentication scheme for TLS sockets. +# +# TLS sockets already have encryption provided by the TLS +# layer, and limited authentication is done by certificates +# +# It is possible to make use of any SASL authentication +# mechanism as well, by using 'sasl' for this option +#auth_tls = "none" + + + +################################################################# +# +# TLS x509 certificate configuration +# + + +# Override the default server key file path +# +#key_file = "/etc/pki/libvirt/private/serverkey.pem" + +# Override the default server certificate file path +# +#cert_file = "/etc/pki/libvirt/servercert.pem" + +# Override the default CA certificate path +# +#ca_file = "/etc/pki/CA/cacert.pem" + +# Specify a certificate revocation list. +# +# Defaults to not using a CRL, uncomment to enable it +#crl_file = "/etc/pki/CA/crl.pem" + + + +################################################################# +# +# Authorization controls +# + + +# Flag to disable verification of client certificates +# +# Client certificate verification is the primary authentication mechanism. +# Any client which does not present a certificate signed by the CA +# will be rejected. +# +# Default is to always verify. Uncommenting this will disable +# verification - make sure an IP whitelist is set +#tls_no_verify_certificate = 1 + + +# A whitelist of allowed x509 Distinguished Names +# This list may contain wildcards such as +# +# "C=GB,ST=London,L=London,O=Red Hat,CN=*" +# +# See the POSIX fnmatch function for the format of the wildcards. +# +# NB If this is an empty list, no client can connect, so comment out +# entirely rather than using empty list to disable these checks +# +# By default, no DN's are checked +#tls_allowed_dn_list = ["DN1", "DN2"] + + +# A whitelist of allowed SASL usernames. The format for usernames +# depends on the SASL authentication mechanism. Kerberos usernames +# look like username@REALM +# +# This list may contain wildcards such as +# +# "*@EXAMPLE.COM" +# +# See the POSIX fnmatch function for the format of the wildcards. +# +# NB If this is an empty list, no client can connect, so comment out +# entirely rather than using empty list to disable these checks +# +# By default, no Username's are checked +#sasl_allowed_username_list = ["joe@EXAMPLE.COM", "fred@EXAMPLE.COM" ] + + diff --git a/logrotate/metadata.json b/logrotate/metadata.json new file mode 100644 index 0000000..e20e2b6 --- /dev/null +++ b/logrotate/metadata.json @@ -0,0 +1,49 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs logrotate", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "logrotate": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "centos": [ + + ], + "debian": [ + + ], + "redhat": [ + + ] + }, + "version": "0.7.0", + "name": "logrotate", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "logrotate": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/logrotate/metadata.rb b/logrotate/metadata.rb new file mode 100644 index 0000000..7714a29 --- /dev/null +++ b/logrotate/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs logrotate" +version "0.7" + +%w{ redhat centos debian ubuntu }.each do |os| + supports os +end diff --git a/logrotate/recipes/default.rb b/logrotate/recipes/default.rb new file mode 100644 index 0000000..b26f7a3 --- /dev/null +++ b/logrotate/recipes/default.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: logrotate +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "logrotate" do + action :upgrade +end diff --git a/logwatch/README.rdoc b/logwatch/README.rdoc new file mode 100644 index 0000000..8d77480 --- /dev/null +++ b/logwatch/README.rdoc @@ -0,0 +1,8 @@ += DESCRIPTION: + += REQUIREMENTS: + += ATTRIBUTES: + += USAGE: + diff --git a/logwatch/metadata.json b/logwatch/metadata.json new file mode 100644 index 0000000..67c9e7f --- /dev/null +++ b/logwatch/metadata.json @@ -0,0 +1,51 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs logwatch, a nice log analyzer", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "logwatch": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "centos": [ + + ], + "debian": [ + + ], + "redhat": [ + + ] + }, + "version": "0.1.0", + "name": "logwatch", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "logwatch": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\n= REQUIREMENTS:\n\n= ATTRIBUTES: \n\n= USAGE:\n\n", + "replacing": { + + }, + "dependencies": { + "perl": [ + + ] + } +} \ No newline at end of file diff --git a/logwatch/metadata.rb b/logwatch/metadata.rb new file mode 100644 index 0000000..75301a8 --- /dev/null +++ b/logwatch/metadata.rb @@ -0,0 +1,12 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs logwatch, a nice log analyzer" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.1" + +depends "perl" + +%w{ redhat centos debian ubuntu }.each do |os| + supports os +end diff --git a/logwatch/recipes/default.rb b/logwatch/recipes/default.rb new file mode 100644 index 0000000..d4f0b89 --- /dev/null +++ b/logwatch/recipes/default.rb @@ -0,0 +1,23 @@ +# +# Cookbook Name:: logwatch +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "perl" + +package "logwatch" + diff --git a/lvm/metadata.json b/lvm/metadata.json new file mode 100644 index 0000000..8390fb3 --- /dev/null +++ b/lvm/metadata.json @@ -0,0 +1,49 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs lvm2 package", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "lvm": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "centos": [ + + ], + "debian": [ + + ], + "redhat": [ + + ] + }, + "version": "0.7.0", + "name": "lvm", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "lvm": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/lvm/metadata.rb b/lvm/metadata.rb new file mode 100644 index 0000000..215ed9b --- /dev/null +++ b/lvm/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs lvm2 package" +version "0.7" + +%w{ redhat centos debian ubuntu }.each do |os| + supports os +end diff --git a/lvm/recipes/default.rb b/lvm/recipes/default.rb new file mode 100644 index 0000000..40dd2a5 --- /dev/null +++ b/lvm/recipes/default.rb @@ -0,0 +1,21 @@ +# +# Cookbook Name:: lvm +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +package "lvm2" do + action :upgrade +end diff --git a/maatkit/files/default/maatkit-5014.tar.gz b/maatkit/files/default/maatkit-5014.tar.gz new file mode 100755 index 0000000000000000000000000000000000000000..f042a9203981b01da60bc2b397daa4a520b096c5 GIT binary patch literal 822002 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 z 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ν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%0DH5^QtO>l!QMaYj?GyByhwk!61bPVTg; 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 z�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

kuQ8fX)$7^=K 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

iSXT0Ma2b~zk(WLP-n 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

O9h)z0--7x~3zGuThBHwquhPVM@O4zqU!) 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 z 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͉mJU!bCj)fjRUPUH0_*> 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

N7g--I&GnhOJlFLrVL< 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 OJpTt1^c80SI57Y`tNpA1 literal 0 HcmV?d00001 diff --git a/maatkit/files/default/mk-query-digest b/maatkit/files/default/mk-query-digest new file mode 100755 index 0000000..21933a8 --- /dev/null +++ b/maatkit/files/default/mk-query-digest @@ -0,0 +1,10308 @@ +#!/usr/bin/env perl + +# This program is copyright 2007-@CURRENTYEAR@ Percona Inc. +# Feedback and improvements are welcome. +# +# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar +# systems, you can issue `man perlgpl' or `man perlartistic' to read these +# licenses. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA. + +use strict; +use warnings FATAL => 'all'; + +our $VERSION = '@VERSION@'; +our $DISTRIB = '@DISTRIB@'; +our $SVN_REV = sprintf("%d", (q$Revision$ =~ m/(\d+)/g, 0)); + +# ########################################################################### +# DSNParser package 5226 +# ########################################################################### +package DSNParser; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); +use Data::Dumper; +$Data::Dumper::Indent = 0; +$Data::Dumper::Quotekeys = 0; + +eval { + require DBI; +}; +my $have_dbi = $EVAL_ERROR ? 0 : 1; + +use constant MKDEBUG => $ENV{MKDEBUG}; + +sub new { + my ( $class, @opts ) = @_; + my $self = { + opts => { + A => { + desc => 'Default character set', + dsn => 'charset', + copy => 1, + }, + D => { + desc => 'Database to use', + dsn => 'database', + copy => 1, + }, + F => { + desc => 'Only read default options from the given file', + dsn => 'mysql_read_default_file', + copy => 1, + }, + h => { + desc => 'Connect to host', + dsn => 'host', + copy => 1, + }, + p => { + desc => 'Password to use when connecting', + dsn => 'password', + copy => 1, + }, + P => { + desc => 'Port number to use for connection', + dsn => 'port', + copy => 1, + }, + S => { + desc => 'Socket file to use for connection', + dsn => 'mysql_socket', + copy => 1, + }, + u => { + desc => 'User for login if not current user', + dsn => 'user', + copy => 1, + }, + }, + }; + foreach my $opt ( @opts ) { + MKDEBUG && _d('Adding extra property', $opt->{key}); + $self->{opts}->{$opt->{key}} = { desc => $opt->{desc}, copy => $opt->{copy} }; + } + return bless $self, $class; +} + +sub prop { + my ( $self, $prop, $value ) = @_; + if ( @_ > 2 ) { + MKDEBUG && _d('Setting', $prop, 'property'); + $self->{$prop} = $value; + } + return $self->{$prop}; +} + +sub parse { + my ( $self, $dsn, $prev, $defaults ) = @_; + if ( !$dsn ) { + MKDEBUG && _d('No DSN to parse'); + return; + } + MKDEBUG && _d('Parsing', $dsn); + $prev ||= {}; + $defaults ||= {}; + my %given_props; + my %final_props; + my %opts = %{$self->{opts}}; + + foreach my $dsn_part ( split(/,/, $dsn) ) { + if ( my ($prop_key, $prop_val) = $dsn_part =~ m/^(.)=(.*)$/ ) { + $given_props{$prop_key} = $prop_val; + } + else { + MKDEBUG && _d('Interpreting', $dsn_part, 'as h=', $dsn_part); + $given_props{h} = $dsn_part; + } + } + + foreach my $key ( keys %opts ) { + MKDEBUG && _d('Finding value for', $key); + $final_props{$key} = $given_props{$key}; + if ( !defined $final_props{$key} + && defined $prev->{$key} && $opts{$key}->{copy} ) + { + $final_props{$key} = $prev->{$key}; + MKDEBUG && _d('Copying value for', $key, 'from previous DSN'); + } + if ( !defined $final_props{$key} ) { + $final_props{$key} = $defaults->{$key}; + MKDEBUG && _d('Copying value for', $key, 'from defaults'); + } + } + + foreach my $key ( keys %given_props ) { + die "Unrecognized DSN part '$key' in '$dsn'\n" + unless exists $opts{$key}; + } + if ( (my $required = $self->prop('required')) ) { + foreach my $key ( keys %$required ) { + die "Missing DSN part '$key' in '$dsn'\n" unless $final_props{$key}; + } + } + + return \%final_props; +} + +sub parse_options { + my ( $self, $o ) = @_; + die 'I need an OptionParser object' unless ref $o eq 'OptionParser'; + my $dsn_string + = join(',', + map { "$_=".$o->get($_); } + grep { $o->has($_) && $o->get($_) } + keys %{$self->{opts}} + ); + MKDEBUG && _d('DSN string made from options:', $dsn_string); + return $self->parse($dsn_string); +} + +sub as_string { + my ( $self, $dsn ) = @_; + return $dsn unless ref $dsn; + return join(',', + map { "$_=" . ($_ eq 'p' ? '...' : $dsn->{$_}) } + grep { defined $dsn->{$_} && $self->{opts}->{$_} } + sort keys %$dsn ); +} + +sub usage { + my ( $self ) = @_; + my $usage + = "DSN syntax is key=value[,key=value...] Allowable DSN keys:\n\n" + . " KEY COPY MEANING\n" + . " === ==== =============================================\n"; + my %opts = %{$self->{opts}}; + foreach my $key ( sort keys %opts ) { + $usage .= " $key " + . ($opts{$key}->{copy} ? 'yes ' : 'no ') + . ($opts{$key}->{desc} || '[No description]') + . "\n"; + } + $usage .= "\n If the DSN is a bareword, the word is treated as the 'h' key.\n"; + return $usage; +} + +sub get_cxn_params { + my ( $self, $info ) = @_; + my $dsn; + my %opts = %{$self->{opts}}; + my $driver = $self->prop('dbidriver') || ''; + if ( $driver eq 'Pg' ) { + $dsn = 'DBI:Pg:dbname=' . ( $info->{D} || '' ) . ';' + . join(';', map { "$opts{$_}->{dsn}=$info->{$_}" } + grep { defined $info->{$_} } + qw(h P)); + } + else { + $dsn = 'DBI:mysql:' . ( $info->{D} || '' ) . ';' + . join(';', map { "$opts{$_}->{dsn}=$info->{$_}" } + grep { defined $info->{$_} } + qw(F h P S A)) + . ';mysql_read_default_group=client'; + } + MKDEBUG && _d($dsn); + return ($dsn, $info->{u}, $info->{p}); +} + +sub fill_in_dsn { + my ( $self, $dbh, $dsn ) = @_; + my $vars = $dbh->selectall_hashref('SHOW VARIABLES', 'Variable_name'); + my ($user, $db) = $dbh->selectrow_array('SELECT USER(), DATABASE()'); + $user =~ s/@.*//; + $dsn->{h} ||= $vars->{hostname}->{Value}; + $dsn->{S} ||= $vars->{'socket'}->{Value}; + $dsn->{P} ||= $vars->{port}->{Value}; + $dsn->{u} ||= $user; + $dsn->{D} ||= $db; +} + +sub get_dbh { + my ( $self, $cxn_string, $user, $pass, $opts ) = @_; + $opts ||= {}; + my $defaults = { + AutoCommit => 0, + RaiseError => 1, + PrintError => 0, + ShowErrorStatement => 1, + mysql_enable_utf8 => ($cxn_string =~ m/charset=utf8/ ? 1 : 0), + }; + @{$defaults}{ keys %$opts } = values %$opts; + + if ( !$have_dbi ) { + die "Cannot connect to MySQL because the Perl DBI module is not " + . "installed or not found. Run 'perl -MDBI' to see the directories " + . "that Perl searches for DBI. If DBI is not installed, try:\n" + . " Debian/Ubuntu apt-get install libdbi-perl\n" + . " RHEL/CentOS yum install perl-DBI\n" + . " OpenSolaris pgk install pkg:/SUNWpmdbi\n"; + + } + + my $dbh; + my $tries = 2; + while ( !$dbh && $tries-- ) { + MKDEBUG && _d($cxn_string, ' ', $user, ' ', $pass, ' {', + join(', ', map { "$_=>$defaults->{$_}" } keys %$defaults ), '}'); + + eval { + $dbh = DBI->connect($cxn_string, $user, $pass, $defaults); + + if ( $cxn_string =~ m/mysql/i ) { + my $sql; + + $sql = q{SET @@SQL_QUOTE_SHOW_CREATE = 1} + . q{/*!40101, @@SQL_MODE='NO_AUTO_VALUE_ON_ZERO'*/}; + MKDEBUG && _d($dbh, ':', $sql); + $dbh->do($sql); + + if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) { + $sql = "/*!40101 SET NAMES $charset*/"; + MKDEBUG && _d($dbh, ':', $sql); + $dbh->do($sql); + MKDEBUG && _d('Enabling charset for STDOUT'); + if ( $charset eq 'utf8' ) { + binmode(STDOUT, ':utf8') + or die "Can't binmode(STDOUT, ':utf8'): $OS_ERROR"; + } + else { + binmode(STDOUT) or die "Can't binmode(STDOUT): $OS_ERROR"; + } + } + + if ( $self->prop('set-vars') ) { + $sql = "SET " . $self->prop('set-vars'); + MKDEBUG && _d($dbh, ':', $sql); + $dbh->do($sql); + } + } + }; + if ( !$dbh && $EVAL_ERROR ) { + MKDEBUG && _d($EVAL_ERROR); + if ( $EVAL_ERROR =~ m/not a compiled character set|character set utf8/ ) { + MKDEBUG && _d('Going to try again without utf8 support'); + delete $defaults->{mysql_enable_utf8}; + } + elsif ( $EVAL_ERROR =~ m/locate DBD\/mysql/i ) { + die "Cannot connect to MySQL because the Perl DBD::mysql module is " + . "not installed or not found. Run 'perl -MDBD::mysql' to see " + . "the directories that Perl searches for DBD::mysql. If " + . "DBD::mysql is not installed, try:\n" + . " Debian/Ubuntu apt-get install libdbd-mysql-perl\n" + . " RHEL/CentOS yum install perl-DBD-MySQL\n" + . " OpenSolaris pgk install pkg:/SUNWapu13dbd-mysql\n"; + } + if ( !$tries ) { + die $EVAL_ERROR; + } + } + } + + MKDEBUG && _d('DBH info: ', + $dbh, + Dumper($dbh->selectrow_hashref( + 'SELECT DATABASE(), CONNECTION_ID(), VERSION()/*!50038 , @@hostname*/')), + 'Connection info:', $dbh->{mysql_hostinfo}, + 'Character set info:', Dumper($dbh->selectall_arrayref( + 'SHOW VARIABLES LIKE "character_set%"', { Slice => {}})), + '$DBD::mysql::VERSION:', $DBD::mysql::VERSION, + '$DBI::VERSION:', $DBI::VERSION, + ); + + return $dbh; +} + +sub get_hostname { + my ( $self, $dbh ) = @_; + if ( my ($host) = ($dbh->{mysql_hostinfo} || '') =~ m/^(\w+) via/ ) { + return $host; + } + my ( $hostname, $one ) = $dbh->selectrow_array( + 'SELECT /*!50038 @@hostname, */ 1'); + return $hostname; +} + +sub disconnect { + my ( $self, $dbh ) = @_; + MKDEBUG && $self->print_active_handles($dbh); + $dbh->disconnect; +} + +sub print_active_handles { + my ( $self, $thing, $level ) = @_; + $level ||= 0; + printf("# Active %sh: %s %s %s\n", ($thing->{Type} || 'undef'), "\t" x $level, + $thing, (($thing->{Type} || '') eq 'st' ? $thing->{Statement} || '' : '')) + or die "Cannot print: $OS_ERROR"; + foreach my $handle ( grep {defined} @{ $thing->{ChildHandles} } ) { + $self->print_active_handles( $handle, $level + 1 ); + } +} + +sub copy { + my ( $self, $dsn_1, $dsn_2, %args ) = @_; + die 'I need a dsn_1 argument' unless $dsn_1; + die 'I need a dsn_2 argument' unless $dsn_2; + my %new_dsn = map { + my $key = $_; + my $val; + if ( $args{overwrite} ) { + $val = defined $dsn_1->{$key} ? $dsn_1->{$key} : $dsn_2->{$key}; + } + else { + $val = defined $dsn_2->{$key} ? $dsn_2->{$key} : $dsn_1->{$key}; + } + $key => $val; + } keys %{$self->{opts}}; + return \%new_dsn; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End DSNParser package +# ########################################################################### + +# ########################################################################### +# Quoter package 4943 +# ########################################################################### +use strict; +use warnings FATAL => 'all'; + +package Quoter; + +use English qw(-no_match_vars); + +use constant MKDEBUG => $ENV{MKDEBUG}; + +sub new { + my ( $class ) = @_; + bless {}, $class; +} + +sub quote { + my ( $self, @vals ) = @_; + foreach my $val ( @vals ) { + $val =~ s/`/``/g; + } + return join('.', map { '`' . $_ . '`' } @vals); +} + +sub quote_val { + my ( $self, $val, $is_numeric ) = @_; + + return 'NULL' unless defined $val; # undef = NULL + return "''" if $val eq ''; # blank string = '' + + if ( !defined $is_numeric ) { + $is_numeric = $val =~ m/^0|\D/ ? 0 : 1; + } + + return $val if $is_numeric; + + $val =~ s/(['\\])/\\$1/g; + return "'$val'"; +} + +sub split_unquote { + my ( $self, $db_tbl, $default_db ) = @_; + $db_tbl =~ s/`//g; + my ( $db, $tbl ) = split(/[.]/, $db_tbl); + if ( !$tbl ) { + $tbl = $db; + $db = $default_db; + } + return ($db, $tbl); +} + +sub literal_like { + my ( $self, $like ) = @_; + return unless $like; + $like =~ s/([%_])/\\$1/g; + return "'$like'"; +} + +1; + +# ########################################################################### +# End Quoter package +# ########################################################################### + +# ########################################################################### +# OptionParser package 4805 +# ########################################################################### +package OptionParser; + +use strict; +use warnings FATAL => 'all'; + +use Getopt::Long; +use List::Util qw(max); +use English qw(-no_match_vars); + +use constant MKDEBUG => $ENV{MKDEBUG}; + +my $POD_link_re = '[LC]<"?([^">]+)"?>'; + +my %attributes = ( + 'type' => 1, + 'short form' => 1, + 'group' => 1, + 'default' => 1, + 'cumulative' => 1, + 'negatable' => 1, +); + +sub new { + my ( $class, %args ) = @_; + foreach my $arg ( qw(description) ) { + die "I need a $arg argument" unless $args{$arg}; + } + my ($program_name) = $PROGRAM_NAME =~ m/([.A-Za-z-]+)$/; + $program_name ||= $PROGRAM_NAME; + my $home = $ENV{HOME} || $ENV{HOMEPATH} || $ENV{USERPROFILE} || '.'; + + my $self = { + description => $args{description}, + prompt => $args{prompt} || '', + strict => (exists $args{strict} ? $args{strict} : 1), + dp => $args{dp} || undef, + program_name => $program_name, + opts => {}, + got_opts => 0, + short_opts => {}, + defaults => {}, + groups => {}, + allowed_groups => {}, + errors => [], + rules => [], # desc of rules for --help + mutex => [], # rule: opts are mutually exclusive + atleast1 => [], # rule: at least one opt is required + disables => {}, # rule: opt disables other opts + defaults_to => {}, # rule: opt defaults to value of other opt + default_files => [ + "/etc/maatkit/maatkit.conf", + "/etc/maatkit/$program_name.conf", + "$home/.maatkit.conf", + "$home/.$program_name.conf", + ], + }; + return bless $self, $class; +} + +sub get_specs { + my ( $self, $file ) = @_; + my @specs = $self->_pod_to_specs($file); + $self->_parse_specs(@specs); + return; +} + +sub get_defaults_files { + my ( $self ) = @_; + return @{$self->{default_files}}; +} + +sub _pod_to_specs { + my ( $self, $file ) = @_; + $file ||= __FILE__; + open my $fh, '<', $file or die "Cannot open $file: $OS_ERROR"; + + my %types = ( + string => 's', # standard Getopt type + 'int' => 'i', # standard Getopt type + float => 'f', # standard Getopt type + Hash => 'H', # hash, formed from a comma-separated list + hash => 'h', # hash as above, but only if a value is given + Array => 'A', # array, similar to Hash + array => 'a', # array, similar to hash + DSN => 'd', # DSN, as provided by a DSNParser which is in $self->{dp} + size => 'z', # size with kMG suffix (powers of 2^10) + 'time' => 'm', # time, with an optional suffix of s/h/m/d + ); + my @specs = (); + my @rules = (); + my $para; + + local $INPUT_RECORD_SEPARATOR = ''; + while ( $para = <$fh> ) { + next unless $para =~ m/^=head1 OPTIONS/; + last; + } + + while ( $para = <$fh> ) { + last if $para =~ m/^=over/; + chomp $para; + $para =~ s/\s+/ /g; + $para =~ s/$POD_link_re/$1/go; + MKDEBUG && _d('Option rule:', $para); + push @rules, $para; + } + + die 'POD has no OPTIONS section' unless $para; + + do { + if ( my ($option) = $para =~ m/^=item --(.*)/ ) { + chomp $para; + MKDEBUG && _d($para); + my %attribs; + + $para = <$fh>; # read next paragraph, possibly attributes + + if ( $para =~ m/: / ) { # attributes + $para =~ s/\s+\Z//g; + %attribs = map { + my ( $attrib, $val) = split(/: /, $_); + die "Unrecognized attribute for --$option: $attrib" + unless $attributes{$attrib}; + ($attrib, $val); + } split(/; /, $para); + if ( $attribs{'short form'} ) { + $attribs{'short form'} =~ s/-//; + } + $para = <$fh>; # read next paragraph, probably short help desc + } + else { + MKDEBUG && _d('Option has no attributes'); + } + + $para =~ s/\s+\Z//g; + $para =~ s/\s+/ /g; + $para =~ s/$POD_link_re/$1/go; + + $para =~ s/\.(?:\n.*| [A-Z].*|\Z)//s; + MKDEBUG && _d('Short help:', $para); + + die "No description after option spec $option" if $para =~ m/^=item/; + + if ( my ($base_option) = $option =~ m/^\[no\](.*)/ ) { + $option = $base_option; + $attribs{'negatable'} = 1; + } + + push @specs, { + spec => $option + . ($attribs{'short form'} ? '|' . $attribs{'short form'} : '' ) + . ($attribs{'negatable'} ? '!' : '' ) + . ($attribs{'cumulative'} ? '+' : '' ) + . ($attribs{'type'} ? '=' . $types{$attribs{type}} : '' ), + desc => $para + . ($attribs{default} ? " (default $attribs{default})" : ''), + group => ($attribs{'group'} ? $attribs{'group'} : 'default'), + }; + } + while ( $para = <$fh> ) { + last unless $para; + + + if ( $para =~ m/^=head1/ ) { + $para = undef; # Can't 'last' out of a do {} block. + last; + } + last if $para =~ m/^=item --/; + } + } while ( $para ); + + die 'No valid specs in POD OPTIONS' unless @specs; + + close $fh; + return @specs, @rules; +} + +sub _parse_specs { + my ( $self, @specs ) = @_; + my %disables; # special rule that requires deferred checking + + foreach my $opt ( @specs ) { + if ( ref $opt ) { # It's an option spec, not a rule. + MKDEBUG && _d('Parsing opt spec:', + map { ($_, '=>', $opt->{$_}) } keys %$opt); + + my ( $long, $short ) = $opt->{spec} =~ m/^([\w-]+)(?:\|([^!+=]*))?/; + if ( !$long ) { + die "Cannot parse long option from spec $opt->{spec}"; + } + $opt->{long} = $long; + + die "Duplicate long option --$long" if exists $self->{opts}->{$long}; + $self->{opts}->{$long} = $opt; + + if ( length $long == 1 ) { + MKDEBUG && _d('Long opt', $long, 'looks like short opt'); + $self->{short_opts}->{$long} = $long; + } + + if ( $short ) { + die "Duplicate short option -$short" + if exists $self->{short_opts}->{$short}; + $self->{short_opts}->{$short} = $long; + $opt->{short} = $short; + } + else { + $opt->{short} = undef; + } + + $opt->{is_negatable} = $opt->{spec} =~ m/!/ ? 1 : 0; + $opt->{is_cumulative} = $opt->{spec} =~ m/\+/ ? 1 : 0; + $opt->{is_required} = $opt->{desc} =~ m/required/ ? 1 : 0; + + $opt->{group} ||= 'default'; + $self->{groups}->{ $opt->{group} }->{$long} = 1; + + $opt->{value} = undef; + $opt->{got} = 0; + + my ( $type ) = $opt->{spec} =~ m/=(.)/; + $opt->{type} = $type; + MKDEBUG && _d($long, 'type:', $type); + + if ( $type && $type eq 'd' && !$self->{dp} ) { + die "$opt->{long} is type DSN (d) but no dp argument " + . "was given when this OptionParser object was created"; + } + + $opt->{spec} =~ s/=./=s/ if ( $type && $type =~ m/[HhAadzm]/ ); + + if ( (my ($def) = $opt->{desc} =~ m/default\b(?: ([^)]+))?/) ) { + $self->{defaults}->{$long} = defined $def ? $def : 1; + MKDEBUG && _d($long, 'default:', $def); + } + + if ( $long eq 'config' ) { + $self->{defaults}->{$long} = join(',', $self->get_defaults_files()); + } + + if ( (my ($dis) = $opt->{desc} =~ m/(disables .*)/) ) { + $disables{$long} = $dis; + MKDEBUG && _d('Deferring check of disables rule for', $opt, $dis); + } + + $self->{opts}->{$long} = $opt; + } + else { # It's an option rule, not a spec. + MKDEBUG && _d('Parsing rule:', $opt); + push @{$self->{rules}}, $opt; + my @participants = $self->_get_participants($opt); + my $rule_ok = 0; + + if ( $opt =~ m/mutually exclusive|one and only one/ ) { + $rule_ok = 1; + push @{$self->{mutex}}, \@participants; + MKDEBUG && _d(@participants, 'are mutually exclusive'); + } + if ( $opt =~ m/at least one|one and only one/ ) { + $rule_ok = 1; + push @{$self->{atleast1}}, \@participants; + MKDEBUG && _d(@participants, 'require at least one'); + } + if ( $opt =~ m/default to/ ) { + $rule_ok = 1; + $self->{defaults_to}->{$participants[0]} = $participants[1]; + MKDEBUG && _d($participants[0], 'defaults to', $participants[1]); + } + if ( $opt =~ m/restricted to option groups/ ) { + $rule_ok = 1; + my ($groups) = $opt =~ m/groups ([\w\s\,]+)/; + my @groups = split(',', $groups); + %{$self->{allowed_groups}->{$participants[0]}} = map { + s/\s+//; + $_ => 1; + } @groups; + } + + die "Unrecognized option rule: $opt" unless $rule_ok; + } + } + + foreach my $long ( keys %disables ) { + my @participants = $self->_get_participants($disables{$long}); + $self->{disables}->{$long} = \@participants; + MKDEBUG && _d('Option', $long, 'disables', @participants); + } + + return; +} + +sub _get_participants { + my ( $self, $str ) = @_; + my @participants; + foreach my $long ( $str =~ m/--(?:\[no\])?([\w-]+)/g ) { + die "Option --$long does not exist while processing rule $str" + unless exists $self->{opts}->{$long}; + push @participants, $long; + } + MKDEBUG && _d('Participants for', $str, ':', @participants); + return @participants; +} + +sub opts { + my ( $self ) = @_; + my %opts = %{$self->{opts}}; + return %opts; +} + +sub short_opts { + my ( $self ) = @_; + my %short_opts = %{$self->{short_opts}}; + return %short_opts; +} + +sub set_defaults { + my ( $self, %defaults ) = @_; + $self->{defaults} = {}; + foreach my $long ( keys %defaults ) { + die "Cannot set default for nonexistent option $long" + unless exists $self->{opts}->{$long}; + $self->{defaults}->{$long} = $defaults{$long}; + MKDEBUG && _d('Default val for', $long, ':', $defaults{$long}); + } + return; +} + +sub get_defaults { + my ( $self ) = @_; + return $self->{defaults}; +} + +sub get_groups { + my ( $self ) = @_; + return $self->{groups}; +} + +sub _set_option { + my ( $self, $opt, $val ) = @_; + my $long = exists $self->{opts}->{$opt} ? $opt + : exists $self->{short_opts}->{$opt} ? $self->{short_opts}->{$opt} + : die "Getopt::Long gave a nonexistent option: $opt"; + + $opt = $self->{opts}->{$long}; + if ( $opt->{is_cumulative} ) { + $opt->{value}++; + } + else { + $opt->{value} = $val; + } + $opt->{got} = 1; + MKDEBUG && _d('Got option', $long, '=', $val); +} + +sub get_opts { + my ( $self ) = @_; + + foreach my $long ( keys %{$self->{opts}} ) { + $self->{opts}->{$long}->{got} = 0; + $self->{opts}->{$long}->{value} + = exists $self->{defaults}->{$long} ? $self->{defaults}->{$long} + : $self->{opts}->{$long}->{is_cumulative} ? 0 + : undef; + } + $self->{got_opts} = 0; + + $self->{errors} = []; + + if ( @ARGV && $ARGV[0] eq "--config" ) { + shift @ARGV; + $self->_set_option('config', shift @ARGV); + } + if ( $self->has('config') ) { + my @extra_args; + foreach my $filename ( split(',', $self->get('config')) ) { + eval { + push @extra_args, $self->_read_config_file($filename); + }; + if ( $EVAL_ERROR ) { + if ( $self->got('config') ) { + die $EVAL_ERROR; + } + elsif ( MKDEBUG ) { + _d($EVAL_ERROR); + } + } + } + unshift @ARGV, @extra_args; + } + + Getopt::Long::Configure('no_ignore_case', 'bundling'); + GetOptions( + map { $_->{spec} => sub { $self->_set_option(@_); } } + grep { $_->{long} ne 'config' } # --config is handled specially above. + values %{$self->{opts}} + ) or $self->save_error('Error parsing options'); + + if ( exists $self->{opts}->{version} && $self->{opts}->{version}->{got} ) { + printf("%s Ver %s Distrib %s Changeset %s\n", + $self->{program_name}, $main::VERSION, $main::DISTRIB, $main::SVN_REV) + or die "Cannot print: $OS_ERROR"; + exit 0; + } + + if ( @ARGV && $self->{strict} ) { + $self->save_error("Unrecognized command-line options @ARGV"); + } + + foreach my $mutex ( @{$self->{mutex}} ) { + my @set = grep { $self->{opts}->{$_}->{got} } @$mutex; + if ( @set > 1 ) { + my $err = join(', ', map { "--$self->{opts}->{$_}->{long}" } + @{$mutex}[ 0 .. scalar(@$mutex) - 2] ) + . ' and --'.$self->{opts}->{$mutex->[-1]}->{long} + . ' are mutually exclusive.'; + $self->save_error($err); + } + } + + foreach my $required ( @{$self->{atleast1}} ) { + my @set = grep { $self->{opts}->{$_}->{got} } @$required; + if ( @set == 0 ) { + my $err = join(', ', map { "--$self->{opts}->{$_}->{long}" } + @{$required}[ 0 .. scalar(@$required) - 2] ) + .' or --'.$self->{opts}->{$required->[-1]}->{long}; + $self->save_error("Specify at least one of $err"); + } + } + + foreach my $long ( keys %{$self->{opts}} ) { + my $opt = $self->{opts}->{$long}; + if ( $opt->{got} ) { + if ( exists $self->{disables}->{$long} ) { + my @disable_opts = @{$self->{disables}->{$long}}; + map { $self->{opts}->{$_}->{value} = undef; } @disable_opts; + MKDEBUG && _d('Unset options', @disable_opts, + 'because', $long,'disables them'); + } + + if ( exists $self->{allowed_groups}->{$long} ) { + + my @restricted_groups = grep { + !exists $self->{allowed_groups}->{$long}->{$_} + } keys %{$self->{groups}}; + + my @restricted_opts; + foreach my $restricted_group ( @restricted_groups ) { + RESTRICTED_OPT: + foreach my $restricted_opt ( + keys %{$self->{groups}->{$restricted_group}} ) + { + next RESTRICTED_OPT if $restricted_opt eq $long; + push @restricted_opts, $restricted_opt + if $self->{opts}->{$restricted_opt}->{got}; + } + } + + if ( @restricted_opts ) { + my $err; + if ( @restricted_opts == 1 ) { + $err = "--$restricted_opts[0]"; + } + else { + $err = join(', ', + map { "--$self->{opts}->{$_}->{long}" } + grep { $_ } + @restricted_opts[0..scalar(@restricted_opts) - 2] + ) + . ' or --'.$self->{opts}->{$restricted_opts[-1]}->{long}; + } + $self->save_error("--$long is not allowed with $err"); + } + } + + } + elsif ( $opt->{is_required} ) { + $self->save_error("Required option --$long must be specified"); + } + + $self->_validate_type($opt); + } + + $self->{got_opts} = 1; + return; +} + +sub _validate_type { + my ( $self, $opt ) = @_; + return unless $opt && $opt->{type}; + my $val = $opt->{value}; + + if ( $val && $opt->{type} eq 'm' ) { # type time + MKDEBUG && _d('Parsing option', $opt->{long}, 'as a time value'); + my ( $prefix, $num, $suffix ) = $val =~ m/([+-]?)(\d+)([a-z])?$/; + if ( !$suffix ) { + my ( $s ) = $opt->{desc} =~ m/\(suffix (.)\)/; + $suffix = $s || 's'; + MKDEBUG && _d('No suffix given; using', $suffix, 'for', + $opt->{long}, '(value:', $val, ')'); + } + if ( $suffix =~ m/[smhd]/ ) { + $val = $suffix eq 's' ? $num # Seconds + : $suffix eq 'm' ? $num * 60 # Minutes + : $suffix eq 'h' ? $num * 3600 # Hours + : $num * 86400; # Days + $opt->{value} = ($prefix || '') . $val; + MKDEBUG && _d('Setting option', $opt->{long}, 'to', $val); + } + else { + $self->save_error("Invalid time suffix for --$opt->{long}"); + } + } + elsif ( $val && $opt->{type} eq 'd' ) { # type DSN + MKDEBUG && _d('Parsing option', $opt->{long}, 'as a DSN'); + my $prev = {}; + my $from_key = $self->{defaults_to}->{ $opt->{long} }; + if ( $from_key ) { + MKDEBUG && _d($opt->{long}, 'DSN copies from', $from_key, 'DSN'); + $prev = $self->{opts}->{$from_key}->{value}; + } + my $defaults = $self->{dp}->parse_options($self); + $opt->{value} = $self->{dp}->parse($val, $prev, $defaults); + } + elsif ( $val && $opt->{type} eq 'z' ) { # type size + MKDEBUG && _d('Parsing option', $opt->{long}, 'as a size value'); + my %factor_for = (k => 1_024, M => 1_048_576, G => 1_073_741_824); + my ($pre, $num, $factor) = $val =~ m/^([+-])?(\d+)([kMG])?$/; + if ( defined $num ) { + if ( $factor ) { + $num *= $factor_for{$factor}; + MKDEBUG && _d('Setting option', $opt->{y}, + 'to num', $num, '* factor', $factor); + } + $opt->{value} = ($pre || '') . $num; + } + else { + $self->save_error("Invalid size for --$opt->{long}"); + } + } + elsif ( $opt->{type} eq 'H' || (defined $val && $opt->{type} eq 'h') ) { + $opt->{value} = { map { $_ => 1 } split(',', ($val || '')) }; + } + elsif ( $opt->{type} eq 'A' || (defined $val && $opt->{type} eq 'a') ) { + $opt->{value} = [ split(/(?{long}, 'type', $opt->{type}, 'value', $val); + } + + return; +} + +sub get { + my ( $self, $opt ) = @_; + my $long = (length $opt == 1 ? $self->{short_opts}->{$opt} : $opt); + die "Option $opt does not exist" + unless $long && exists $self->{opts}->{$long}; + return $self->{opts}->{$long}->{value}; +} + +sub got { + my ( $self, $opt ) = @_; + my $long = (length $opt == 1 ? $self->{short_opts}->{$opt} : $opt); + die "Option $opt does not exist" + unless $long && exists $self->{opts}->{$long}; + return $self->{opts}->{$long}->{got}; +} + +sub has { + my ( $self, $opt ) = @_; + my $long = (length $opt == 1 ? $self->{short_opts}->{$opt} : $opt); + return defined $long ? exists $self->{opts}->{$long} : 0; +} + +sub set { + my ( $self, $opt, $val ) = @_; + my $long = (length $opt == 1 ? $self->{short_opts}->{$opt} : $opt); + die "Option $opt does not exist" + unless $long && exists $self->{opts}->{$long}; + $self->{opts}->{$long}->{value} = $val; + return; +} + +sub save_error { + my ( $self, $error ) = @_; + push @{$self->{errors}}, $error; +} + +sub errors { + my ( $self ) = @_; + return $self->{errors}; +} + +sub prompt { + my ( $self ) = @_; + return "Usage: $PROGRAM_NAME $self->{prompt}\n"; +} + +sub descr { + my ( $self ) = @_; + my $descr = $self->{program_name} . ' ' . ($self->{description} || '') + . " For more details, please use the --help option, " + . "or try 'perldoc $PROGRAM_NAME' " + . "for complete documentation."; + $descr = join("\n", $descr =~ m/(.{0,80})(?:\s+|$)/g); + $descr =~ s/ +$//mg; + return $descr; +} + +sub usage_or_errors { + my ( $self ) = @_; + if ( $self->{opts}->{help}->{got} ) { + print $self->print_usage() or die "Cannot print usage: $OS_ERROR"; + exit 0; + } + elsif ( scalar @{$self->{errors}} ) { + print $self->print_errors() or die "Cannot print errors: $OS_ERROR"; + exit 0; + } + return; +} + +sub print_errors { + my ( $self ) = @_; + my $usage = $self->prompt() . "\n"; + if ( (my @errors = @{$self->{errors}}) ) { + $usage .= join("\n * ", 'Errors in command-line arguments:', @errors) + . "\n"; + } + return $usage . "\n" . $self->descr(); +} + +sub print_usage { + my ( $self ) = @_; + die "Run get_opts() before print_usage()" unless $self->{got_opts}; + my @opts = values %{$self->{opts}}; + + my $maxl = max( + map { length($_->{long}) + ($_->{is_negatable} ? 4 : 0) } + @opts); + + my $maxs = max(0, + map { length($_) + ($self->{opts}->{$_}->{is_negatable} ? 4 : 0) } + values %{$self->{short_opts}}); + + my $lcol = max($maxl, ($maxs + 3)); + my $rcol = 80 - $lcol - 6; + my $rpad = ' ' x ( 80 - $rcol ); + + $maxs = max($lcol - 3, $maxs); + + my $usage = $self->descr() . "\n" . $self->prompt(); + + my @groups = reverse sort grep { $_ ne 'default'; } keys %{$self->{groups}}; + push @groups, 'default'; + + foreach my $group ( reverse @groups ) { + $usage .= "\n".($group eq 'default' ? 'Options' : $group).":\n\n"; + foreach my $opt ( + sort { $a->{long} cmp $b->{long} } + grep { $_->{group} eq $group } + @opts ) + { + my $long = $opt->{is_negatable} ? "[no]$opt->{long}" : $opt->{long}; + my $short = $opt->{short}; + my $desc = $opt->{desc}; + if ( $opt->{type} && $opt->{type} eq 'm' ) { + my ($s) = $desc =~ m/\(suffix (.)\)/; + $s ||= 's'; + $desc =~ s/\s+\(suffix .\)//; + $desc .= ". Optional suffix s=seconds, m=minutes, h=hours, " + . "d=days; if no suffix, $s is used."; + } + $desc = join("\n$rpad", grep { $_ } $desc =~ m/(.{0,$rcol})(?:\s+|$)/g); + $desc =~ s/ +$//mg; + if ( $short ) { + $usage .= sprintf(" --%-${maxs}s -%s %s\n", $long, $short, $desc); + } + else { + $usage .= sprintf(" --%-${lcol}s %s\n", $long, $desc); + } + } + } + + if ( (my @rules = @{$self->{rules}}) ) { + $usage .= "\nRules:\n\n"; + $usage .= join("\n", map { " $_" } @rules) . "\n"; + } + if ( $self->{dp} ) { + $usage .= "\n" . $self->{dp}->usage(); + } + $usage .= "\nOptions and values after processing arguments:\n\n"; + foreach my $opt ( sort { $a->{long} cmp $b->{long} } @opts ) { + my $val = $opt->{value}; + my $type = $opt->{type} || ''; + my $bool = $opt->{spec} =~ m/^[\w-]+(?:\|[\w-])?!?$/; + $val = $bool ? ( $val ? 'TRUE' : 'FALSE' ) + : !defined $val ? '(No value)' + : $type eq 'd' ? $self->{dp}->as_string($val) + : $type =~ m/H|h/ ? join(',', sort keys %$val) + : $type =~ m/A|a/ ? join(',', @$val) + : $val; + $usage .= sprintf(" --%-${lcol}s %s\n", $opt->{long}, $val); + } + return $usage; +} + +sub prompt_noecho { + shift @_ if ref $_[0] eq __PACKAGE__; + my ( $prompt ) = @_; + local $OUTPUT_AUTOFLUSH = 1; + print $prompt + or die "Cannot print: $OS_ERROR"; + my $response; + eval { + require Term::ReadKey; + Term::ReadKey::ReadMode('noecho'); + chomp($response = ); + Term::ReadKey::ReadMode('normal'); + print "\n" + or die "Cannot print: $OS_ERROR"; + }; + if ( $EVAL_ERROR ) { + die "Cannot read response; is Term::ReadKey installed? $EVAL_ERROR"; + } + return $response; +} + +if ( MKDEBUG ) { + print '# ', $^X, ' ', $], "\n"; + my $uname = `uname -a`; + if ( $uname ) { + $uname =~ s/\s+/ /g; + print "# $uname\n"; + } + printf("# %s Ver %s Distrib %s Changeset %s line %d\n", + $PROGRAM_NAME, ($main::VERSION || ''), ($main::DISTRIB || ''), + ($main::SVN_REV || ''), __LINE__); + print('# Arguments: ', + join(' ', map { my $a = "_[$_]_"; $a =~ s/\n/\n# /g; $a; } @ARGV), "\n"); +} + +sub _read_config_file { + my ( $self, $filename ) = @_; + open my $fh, "<", $filename or die "Cannot open $filename: $OS_ERROR\n"; + my @args; + my $prefix = '--'; + my $parse = 1; + + LINE: + while ( my $line = <$fh> ) { + chomp $line; + next LINE if $line =~ m/^\s*(?:\#|\;|$)/; + $line =~ s/\s+#.*$//g; + $line =~ s/^\s+|\s+$//g; + if ( $line eq '--' ) { + $prefix = ''; + $parse = 0; + next LINE; + } + if ( $parse + && (my($opt, $arg) = $line =~ m/^\s*([^=\s]+?)(?:\s*=\s*(.*?)\s*)?$/) + ) { + push @args, grep { defined $_ } ("$prefix$opt", $arg); + } + elsif ( $line =~ m/./ ) { + push @args, $line; + } + else { + die "Syntax error in file $filename at line $INPUT_LINE_NUMBER"; + } + } + close $fh; + return @args; +} + +sub read_para_after { + my ( $self, $file, $regex ) = @_; + open my $fh, "<", $file or die "Can't open $file: $OS_ERROR"; + local $INPUT_RECORD_SEPARATOR = ''; + my $para; + while ( $para = <$fh> ) { + next unless $para =~ m/^=pod$/m; + last; + } + while ( $para = <$fh> ) { + next unless $para =~ m/$regex/; + last; + } + $para = <$fh>; + chomp($para); + close $fh or die "Can't close $file: $OS_ERROR"; + return $para; +} + +sub clone { + my ( $self ) = @_; + + my %clone = map { + my $hashref = $self->{$_}; + my $val_copy = {}; + foreach my $key ( keys %$hashref ) { + my $ref = ref $hashref->{$key}; + $val_copy->{$key} = !$ref ? $hashref->{$key} + : $ref eq 'HASH' ? { %{$hashref->{$key}} } + : $ref eq 'ARRAY' ? [ @{$hashref->{$key}} ] + : $hashref->{$key}; + } + $_ => $val_copy; + } qw(opts short_opts defaults); + + foreach my $scalar ( qw(got_opts) ) { + $clone{$scalar} = $self->{$scalar}; + } + + return bless \%clone; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End OptionParser package +# ########################################################################### + +# ########################################################################### +# Transformers package 5144 +# ########################################################################### + +package Transformers; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); +use Time::Local qw(timegm timelocal); +use Digest::MD5 qw(md5_hex); + +use constant MKDEBUG => $ENV{MKDEBUG}; + +require Exporter; +our @ISA = qw(Exporter); +our %EXPORT_TAGS = (); +our @EXPORT = (); +our @EXPORT_OK = qw( + micro_t + percentage_of + secs_to_time + shorten + ts + parse_timestamp + unix_timestamp + any_unix_timestamp + make_checksum +); + +our $mysql_ts = qr/(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)(\.\d+)?/; +our $proper_ts = qr/(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)(\.\d+)?/; +our $n_ts = qr/(\d{1,5})([shmd]?)/; # Limit \d{1,5} because \d{6} looks + +sub micro_t { + my ( $t, %args ) = @_; + my $p_ms = defined $args{p_ms} ? $args{p_ms} : 0; # precision for ms vals + my $p_s = defined $args{p_s} ? $args{p_s} : 0; # precision for s vals + my $f; + + $t = 0 if $t < 0; + + $t = sprintf('%.17f', $t) if $t =~ /e/; + + $t =~ s/\.(\d{1,6})\d*/\.$1/; + + if ($t > 0 && $t <= 0.000999) { + $f = ($t * 1000000) . 'us'; + } + elsif ($t >= 0.001000 && $t <= 0.999999) { + $f = sprintf("%.${p_ms}f", $t * 1000); + $f = ($f * 1) . 'ms'; # * 1 to remove insignificant zeros + } + elsif ($t >= 1) { + $f = sprintf("%.${p_s}f", $t); + $f = ($f * 1) . 's'; # * 1 to remove insignificant zeros + } + else { + $f = 0; # $t should = 0 at this point + } + + return $f; +} + +sub percentage_of { + my ( $is, $of, %args ) = @_; + my $p = $args{p} || 0; # float precision + my $fmt = $p ? "%.${p}f" : "%d"; + return sprintf $fmt, ($is * 100) / ($of ||= 1); +} + +sub secs_to_time { + my ( $secs, $fmt ) = @_; + $secs ||= 0; + return '00:00' unless $secs; + + $fmt ||= $secs >= 86_400 ? 'd' + : $secs >= 3_600 ? 'h' + : 'm'; + + return + $fmt eq 'd' ? sprintf( + "%d+%02d:%02d:%02d", + int($secs / 86_400), + int(($secs % 86_400) / 3_600), + int(($secs % 3_600) / 60), + $secs % 60) + : $fmt eq 'h' ? sprintf( + "%02d:%02d:%02d", + int(($secs % 86_400) / 3_600), + int(($secs % 3_600) / 60), + $secs % 60) + : sprintf( + "%02d:%02d", + int(($secs % 3_600) / 60), + $secs % 60); +} + +sub shorten { + my ( $num, %args ) = @_; + my $p = defined $args{p} ? $args{p} : 2; # float precision + my $d = defined $args{d} ? $args{d} : 1_024; # divisor + my $n = 0; + my @units = ('', qw(k M G T P E Z Y)); + while ( $num >= $d && $n < @units - 1 ) { + $num /= $d; + ++$n; + } + return sprintf( + $num =~ m/\./ || $n + ? "%.${p}f%s" + : '%d', + $num, $units[$n]); +} + +sub ts { + my ( $time, $gmt ) = @_; + my ( $sec, $min, $hour, $mday, $mon, $year ) + = $gmt ? gmtime($time) : localtime($time); + $mon += 1; + $year += 1900; + my $val = sprintf("%d-%02d-%02dT%02d:%02d:%02d", + $year, $mon, $mday, $hour, $min, $sec); + if ( my ($us) = $time =~ m/(\.\d+)$/ ) { + $us = sprintf("%.6f", $us); + $us =~ s/^0\././; + $val .= $us; + } + return $val; +} + +sub parse_timestamp { + my ( $val ) = @_; + if ( my($y, $m, $d, $h, $i, $s, $f) + = $val =~ m/^$mysql_ts$/ ) + { + return sprintf "%d-%02d-%02d %02d:%02d:" + . (defined $f ? '%02.6f' : '%02d'), + $y + 2000, $m, $d, $h, $i, (defined $f ? $s + $f : $s); + } + return $val; +} + +sub unix_timestamp { + my ( $val, $gmt ) = @_; + if ( my($y, $m, $d, $h, $i, $s, $us) = $val =~ m/^$proper_ts$/ ) { + $val = $gmt + ? timegm($s, $i, $h, $d, $m - 1, $y) + : timelocal($s, $i, $h, $d, $m - 1, $y); + if ( defined $us ) { + $us = sprintf('%.6f', $us); + $us =~ s/^0\././; + $val .= $us; + } + } + return $val; +} + +sub any_unix_timestamp { + my ( $val, $callback ) = @_; + + if ( my ($n, $suffix) = $val =~ m/^$n_ts$/ ) { + $n = $suffix eq 's' ? $n # Seconds + : $suffix eq 'm' ? $n * 60 # Minutes + : $suffix eq 'h' ? $n * 3600 # Hours + : $suffix eq 'd' ? $n * 86400 # Days + : $n; # default: Seconds + MKDEBUG && _d('ts is now - N[shmd]:', $n); + return time - $n; + } + elsif ( my ($ymd, $hms) = $val =~ m/^(\d{6})(?:\s+(\d+:\d+:\d+))?/ ) { + MKDEBUG && _d('ts is MySQL slow log timestamp'); + $val .= ' 00:00:00' unless $hms; + return unix_timestamp(parse_timestamp($val)); + } + elsif ( ($ymd, $hms) = $val =~ m/^(\d{4}-\d\d-\d\d)(?:[T ](\d+:\d+:\d+))?/) { + MKDEBUG && _d('ts is properly formatted timestamp'); + $val .= ' 00:00:00' unless $hms; + return unix_timestamp($val); + } + else { + MKDEBUG && _d('ts is MySQL expression'); + return $callback->($val) if $callback && ref $callback eq 'CODE'; + } + + MKDEBUG && _d('Unknown ts type:', $val); + return; +} + +sub make_checksum { + my ( $val ) = @_; + my $checksum = uc substr(md5_hex($val), -16); + MKDEBUG && _d($checksum, 'checksum for', $val); + return $checksum; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End Transformers package +# ########################################################################### + +# ########################################################################### +# QueryRewriter package 5228 +# ########################################################################### +use strict; +use warnings FATAL => 'all'; + +package QueryRewriter; + +use English qw(-no_match_vars); + +use constant MKDEBUG => $ENV{MKDEBUG}; + +our $verbs = qr{^SHOW|^FLUSH|^COMMIT|^ROLLBACK|^BEGIN|SELECT|INSERT + |UPDATE|DELETE|REPLACE|^SET|UNION|^START|^LOCK}xi; +my $quote_re = qr/"(?:(?!(? [^()]+ ) # Non-parens without backtracking + | + (??{ $bal }) # Group with matching parens + )* + \) + /x; + +my $olc_re = qr/(?:--|#)[^'"\r\n]*(?=[\r\n]|\Z)/; # One-line comments +my $mlc_re = qr#/\*[^!].*?\*/#sm; # But not /*!version */ + +sub new { + my ( $class, %args ) = @_; + my $self = { %args }; + return bless $self, $class; +} + +sub strip_comments { + my ( $self, $query ) = @_; + $query =~ s/$olc_re//go; + $query =~ s/$mlc_re//go; + return $query; +} + +sub shorten { + my ( $self, $query, $length ) = @_; + $query =~ s{ + \A( + (?:INSERT|REPLACE) + (?:\s+LOW_PRIORITY|DELAYED|HIGH_PRIORITY|IGNORE)? + (?:\s\w+)*\s+\S+\s+VALUES\s*\(.*?\) + ) + \s*,\s*\(.*?(ON\s+DUPLICATE|\Z)} + {$1 /*... omitted ...*/$2}xsi; + + return $query unless $query =~ m/IN\s*\(\s*(?!select)/i; + + my $last_length = 0; + my $query_length = length($query); + while ( + $length > 0 + && $query_length > $length + && $query_length < ( $last_length || $query_length + 1 ) + ) { + $last_length = $query_length; + $query =~ s{ + (\bIN\s*\() # The opening of an IN list + ([^\)]+) # Contents of the list, assuming no item contains paren + (?=\)) # Close of the list + } + { + $1 . __shorten($2) + }gexsi; + } + + return $query; +} + +sub __shorten { + my ( $snippet ) = @_; + my @vals = split(/,/, $snippet); + return $snippet unless @vals > 20; + my @keep = splice(@vals, 0, 20); # Remove and save the first 20 items + return + join(',', @keep) + . "/*... omitted " + . scalar(@vals) + . " items ...*/"; +} + +sub fingerprint { + my ( $self, $query ) = @_; + + $query =~ m#\ASELECT /\*!40001 SQL_NO_CACHE \*/ \* FROM `# # mysqldump query + && return 'mysqldump'; + $query =~ m#/\*\w+\.\w+:[0-9]/[0-9]\*/# # mk-table-checksum, etc query + && return 'maatkit'; + $query =~ m/\A# administrator command: / + && return $query; + $query =~ m/\A\s*(call\s+\S+)\(/i + && return lc($1); # Warning! $1 used, be careful. + if ( my ($beginning) = $query =~ m/\A((?:INSERT|REPLACE)(?: IGNORE)?\s+INTO.+?VALUES\s*\(.*?\))\s*,\s*\(/is ) { + $query = $beginning; # Shorten multi-value INSERT statements ASAP + } + + $query =~ s/$olc_re//go; + $query =~ s/$mlc_re//go; + $query =~ s/\Ause \S+\Z/use ?/i # Abstract the DB in USE + && return $query; + + $query =~ s/\\["']//g; # quoted strings + $query =~ s/".*?"/?/sg; # quoted strings + $query =~ s/'.*?'/?/sg; # quoted strings + $query =~ s/[0-9+-][0-9a-f.xb+-]*/?/g;# Anything vaguely resembling numbers + $query =~ s/[xb.+-]\?/?/g; # Clean up leftovers + $query =~ s/\A\s+//; # Chop off leading whitespace + chomp $query; # Kill trailing whitespace + $query =~ tr[ \n\t\r\f][ ]s; # Collapse whitespace + $query = lc $query; + $query =~ s/\bnull\b/?/g; # Get rid of NULLs + $query =~ s{ # Collapse IN and VALUES lists + \b(in|values?)(?:[\s,]*\([\s?,]*\))+ + } + {$1(?+)}gx; + $query =~ s{ # Collapse UNION + \b(select\s.*?)(?:(\sunion(?:\sall)?)\s\1)+ + } + {$1 /*repeat$2*/}xg; + $query =~ s/\blimit \?(?:, ?\?| offset \?)?/limit ?/; # LIMIT + return $query; +} + +sub _distill_verbs { + my ( $self, $query ) = @_; + + $query =~ m/\A\s*call\s+(\S+)\(/i + && return "CALL $1"; # Warning! $1 used, be careful. + $query =~ m/\A# administrator/ + && return "ADMIN"; + $query =~ m/\A\s*use\s+/ + && return "USE"; + $query =~ m/\A\s*UNLOCK TABLES/i + && return "UNLOCK"; + $query =~ m/\A\s*xa\s+(\S+)/i + && return "XA_$1"; + + $query = $self->strip_comments($query); + + if ( $query =~ m/\A\s*SHOW\s+/i ) { + my @what = $query =~ m/SHOW\s+(\S+)(?:\s+(\S+))?/i; + MKDEBUG && _d('SHOW', @what); + return unless scalar @what; + @what = map { uc $_ } grep { defined $_ } @what; + if ( $what[0] =~ m/CREATE/ || ($what[1] && $what[1] =~ m/STATUS/) ) { + return "SHOW $what[0] $what[1]"; + } + else { + return "SHOW $what[0]"; + } + } + + eval $QueryParser::data_def_stmts; + eval $QueryParser::tbl_ident; + my ( $dds ) = $query =~ /^\s*($QueryParser::data_def_stmts)\b/i; + if ( $dds ) { + my ( $obj ) = $query =~ m/$dds.+(DATABASE|TABLE)\b/i; + $obj = uc $obj if $obj; + MKDEBUG && _d('Data def statment:', $dds, 'obj:', $obj); + my ($db_or_tbl) + = $query =~ m/(?:TABLE|DATABASE)\s+($QueryParser::tbl_ident)(\s+.*)?/i; + MKDEBUG && _d('Matches db or table:', $db_or_tbl); + return uc($dds . ($obj ? " $obj" : '')), $db_or_tbl; + } + + my @verbs = $query =~ m/\b($verbs)\b/gio; + @verbs = do { + my $last = ''; + grep { my $pass = $_ ne $last; $last = $_; $pass } map { uc } @verbs; + }; + my $verbs = join(q{ }, @verbs); + $verbs =~ s/( UNION SELECT)+/ UNION/g; + + return $verbs; +} + +sub _distill_tables { + my ( $self, $query, $table, %args ) = @_; + my $qp = $args{QueryParser} || $self->{QueryParser}; + die "I need a QueryParser argument" unless $qp; + + my @tables = map { + $_ =~ s/`//g; + $_ =~ s/(_?)[0-9]+/$1?/g; + $_; + } grep { defined $_ } $qp->get_tables($query); + + push @tables, $table if $table; + + @tables = do { + my $last = ''; + grep { my $pass = $_ ne $last; $last = $_; $pass } @tables; + }; + + return @tables; +} + +sub distill { + my ( $self, $query, %args ) = @_; + + if ( $args{generic} ) { + my ($cmd, $arg) = $query =~ m/^(\S+)\s+(\S+)/; + return '' unless $cmd; + $query = (uc $cmd) . ($arg ? " $arg" : ''); + } + else { + my ($verbs, $table) = $self->_distill_verbs($query, %args); + my @tables = $self->_distill_tables($query, $table, %args); + $query = join(q{ }, $verbs, @tables); + } + + if ( $args{trf} ) { + $query = $args{trf}->($query, %args); + } + + return $query; +} + +sub convert_to_select { + my ( $self, $query ) = @_; + return unless $query; + $query =~ s{ + \A.*? + update\s+(.*?) + \s+set\b(.*?) + (?:\s*where\b(.*?))? + (limit\s*[0-9]+(?:\s*,\s*[0-9]+)?)? + \Z + } + {__update_to_select($1, $2, $3, $4)}exsi + || $query =~ s{ + \A.*? + (?:insert|replace)\s+ + .*?\binto\b(.*?)\(([^\)]+)\)\s* + values?\s*(\(.*?\))\s* + (?:\blimit\b|on\s*duplicate\s*key.*)?\s* + \Z + } + {__insert_to_select($1, $2, $3)}exsi + || $query =~ s{ + \A.*? + delete\s+(.*?) + \bfrom\b(.*) + \Z + } + {__delete_to_select($1, $2)}exsi; + $query =~ s/\s*on\s+duplicate\s+key\s+update.*\Z//si; + $query =~ s/\A.*?(?=\bSELECT\s*\b)//ism; + return $query; +} + +sub convert_select_list { + my ( $self, $query ) = @_; + $query =~ s{ + \A\s*select(.*?)\bfrom\b + } + {$1 =~ m/\*/ ? "select 1 from" : "select isnull(coalesce($1)) from"}exi; + return $query; +} + +sub __delete_to_select { + my ( $delete, $join ) = @_; + if ( $join =~ m/\bjoin\b/ ) { + return "select 1 from $join"; + } + return "select * from $join"; +} + +sub __insert_to_select { + my ( $tbl, $cols, $vals ) = @_; + MKDEBUG && _d('Args:', @_); + my @cols = split(/,/, $cols); + MKDEBUG && _d('Cols:', @cols); + $vals =~ s/^\(|\)$//g; # Strip leading/trailing parens + my @vals = $vals =~ m/($quote_re|[^,]*${bal}[^,]*|[^,]+)/g; + MKDEBUG && _d('Vals:', @vals); + if ( @cols == @vals ) { + return "select * from $tbl where " + . join(' and ', map { "$cols[$_]=$vals[$_]" } (0..$#cols)); + } + else { + return "select * from $tbl limit 1"; + } +} + +sub __update_to_select { + my ( $from, $set, $where, $limit ) = @_; + return "select $set from $from " + . ( $where ? "where $where" : '' ) + . ( $limit ? " $limit " : '' ); +} + +sub wrap_in_derived { + my ( $self, $query ) = @_; + return unless $query; + return $query =~ m/\A\s*select/i + ? "select 1 from ($query) as x limit 1" + : $query; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End QueryRewriter package +# ########################################################################### + +# ########################################################################### +# Processlist package 5148 +# ########################################################################### +package Processlist; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); + +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; +$Data::Dumper::Quotekeys = 0; + +use constant MKDEBUG => $ENV{MKDEBUG}; +use constant { + ID => 0, + USER => 1, + HOST => 2, + DB => 3, + COMMAND => 4, + TIME => 5, + STATE => 6, + INFO => 7, + START => 8, # Calculated start time of statement + ETIME => 9, # Exec time of SHOW PROCESSLIST (margin of error in START) + FSEEN => 10, # First time ever seen +}; + +sub new { + my ( $class, %args ) = @_; + my $self = { + prev_rows => [], + new_rows => [], + curr_row => undef, + prev_row => undef, + }; + return bless $self, $class; +} + +sub parse_event { + my ( $self, %args ) = @_; + my @required_args = qw(misc); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my ($misc) = @args{@required_args}; + + my $code = $misc->{code}; + die "I need a code arg to misc" unless $code; + + my @curr; + if ( $self->{curr_rows} ) { + MKDEBUG && _d('Current rows from last call'); + @curr = @{$self->{curr_rows}}; + } + else { + my $rows = $code->(); + if ( $rows && scalar @$rows ) { + MKDEBUG && _d('Got new current rows'); + @curr = sort { $a->[ID] <=> $b->[ID] } @$rows; + } + else { + MKDEBUG && _d('No current rows'); + } + } + + my @prev = @{$self->{prev_rows} ||= []}; + my @new = @{$self->{new_rows} ||= []};; # Becomes next invocation's @prev + my $curr = $self->{curr_row}; # Rows from each source + my $prev = $self->{prev_row}; + my $event; + + MKDEBUG && _d('Rows:', scalar @prev, 'prev,', scalar @curr, 'current'); + + if ( !$curr && @curr ) { + MKDEBUG && _d('Fetching row from curr'); + $curr = shift @curr; + } + if ( !$prev && @prev ) { + MKDEBUG && _d('Fetching row from prev'); + $prev = shift @prev; + } + if ( $curr || $prev ) { + if ( $curr && $prev && $curr->[ID] == $prev->[ID] ) { + MKDEBUG && _d('$curr and $prev are the same cxn'); + my $fudge = $curr->[TIME] =~ m/\D/ ? 0.001 : 1; # Micro-precision? + my $is_new = 0; + if ( $prev->[INFO] ) { + if (!$curr->[INFO] || $prev->[INFO] ne $curr->[INFO]) { + MKDEBUG && _d('$curr has a new query'); + $is_new = 1; + } + elsif (defined $curr->[TIME] && $curr->[TIME] < $prev->[TIME]) { + MKDEBUG && _d('$curr time is less than $prev time'); + $is_new = 1; + } + elsif ( $curr->[INFO] && defined $curr->[TIME] + && $misc->{time} - $curr->[TIME] - $prev->[START] + - $prev->[ETIME] - $misc->{etime} > $fudge + ) { + MKDEBUG && _d('$curr has same query that restarted'); + $is_new = 1; + } + if ( $is_new ) { + $event = $self->make_event($prev, $misc->{time}); + } + } + if ( $curr->[INFO] ) { + if ( $prev->[INFO] && !$is_new ) { + MKDEBUG && _d('Pushing old history item back onto $prev'); + push @new, [ @$prev ]; + } + else { + MKDEBUG && _d('Pushing new history item onto $prev'); + push @new, + [ @$curr, int($misc->{time} - $curr->[TIME]), + $misc->{etime}, $misc->{time} ]; + } + } + $curr = $prev = undef; # Fetch another from each. + } + elsif ( !$curr + || ($curr && $prev && $curr->[ID] > $prev->[ID]) ) { + MKDEBUG && _d('$curr is not in $prev'); + $event = $self->make_event($prev, $misc->{time}); + $prev = undef; + } + else { # This else must be entered, to prevent infinite loops. + MKDEBUG && _d('$prev is not in $curr'); + if ( $curr->[INFO] && defined $curr->[TIME] ) { + MKDEBUG && _d('Pushing new history item onto $prev'); + push @new, + [ @$curr, int($misc->{time} - $curr->[TIME]), + $misc->{etime}, $misc->{time} ]; + } + $curr = undef; # No infinite loops. + } + } + + $self->{prev_rows} = \@new; + $self->{prev_row} = $prev; + $self->{curr_rows} = scalar @curr ? \@curr : undef; + $self->{curr_row} = $curr; + + return $event; +} + +sub make_event { + my ( $self, $row, $time ) = @_; + my $Query_time = $row->[TIME]; + if ( $row->[TIME] < $time - $row->[FSEEN] ) { + $Query_time = $time - $row->[FSEEN]; + } + my $event = { + id => $row->[ID], + db => $row->[DB], + user => $row->[USER], + host => $row->[HOST], + arg => $row->[INFO], + bytes => length($row->[INFO]), + ts => Transformers::ts($row->[START] + $row->[TIME]), # Query END time + Query_time => $Query_time, + Lock_time => 0, # TODO + }; + MKDEBUG && _d('Properties of event:', Dumper($event)); + return $event; +} + +sub _get_rows { + my ( $self ) = @_; + my %rows = map { $_ => $self->{$_} } + qw(prev_rows new_rows curr_row prev_row); + return \%rows; +} + +sub find { + my ( $self, $proclist, %find_spec ) = @_; + MKDEBUG && _d('find specs:', Dumper(\%find_spec)); + my @matches; + QUERY: + foreach my $query ( @$proclist ) { + MKDEBUG && _d('Checking query', Dumper($query)); + my $matched = 0; + + if ( $find_spec{busy_time} && ($query->{Command} || '') eq 'Query' ) { + if ( $query->{Time} < $find_spec{busy_time} ) { + MKDEBUG && _d("Query isn't running long enough"); + next QUERY; + } + MKDEBUG && _d('Exceeds busy time'); + $matched++; + } + + if ( $find_spec{idle_time} && ($query->{Command} || '') eq 'Sleep' ) { + if ( $query->{Time} < $find_spec{idle_time} ) { + MKDEBUG && _d("Query isn't idle long enough"); + next QUERY; + } + MKDEBUG && _d('Exceeds idle time'); + $matched++; + } + + PROPERTY: + foreach my $property ( qw(Id User Host db State Command Info) ) { + my $filter = "_find_match_$property"; + if ( defined $find_spec{ignore}->{$property} + && $self->$filter($query, $find_spec{ignore}->{$property}) ) { + MKDEBUG && _d('Query matches ignore', $property, 'spec'); + next QUERY; + } + if ( defined $find_spec{match}->{$property} ) { + if ( !$self->$filter($query, $find_spec{match}->{$property}) ) { + MKDEBUG && _d('Query does not match', $property, 'spec'); + next QUERY; + } + MKDEBUG && _d('Query matches', $property, 'spec'); + $matched++; + } + } + if ( $matched ) { + MKDEBUG && _d("Query matched one or more specs, adding"); + push @matches, $query; + next QUERY; + } + MKDEBUG && _d('Query does not match any specs, ignoring'); + } # QUERY + + if ( @matches && $find_spec{only_oldest} ) { + my ( $oldest ) = reverse sort { $a->{Time} <=> $b->{Time} } @matches; + MKDEBUG && _d('Oldest query:', Dumper($oldest)); + @matches = $oldest; + } + + return @matches; +} + +sub _find_match_Id { + my ( $self, $query, $property ) = @_; + return defined $property && defined $query->{Id} && $query->{Id} == $property; +} + +sub _find_match_User { + my ( $self, $query, $property ) = @_; + return defined $property && defined $query->{User} + && $query->{User} =~ m/$property/; +} + +sub _find_match_Host { + my ( $self, $query, $property ) = @_; + return defined $property && defined $query->{Host} + && $query->{Host} =~ m/$property/; +} + +sub _find_match_db { + my ( $self, $query, $property ) = @_; + return defined $property && defined $query->{db} + && $query->{db} =~ m/$property/; +} + +sub _find_match_State { + my ( $self, $query, $property ) = @_; + return defined $property && defined $query->{State} + && $query->{State} =~ m/$property/; +} + +sub _find_match_Command { + my ( $self, $query, $property ) = @_; + return defined $property && defined $query->{Command} + && $query->{Command} =~ m/$property/; +} + +sub _find_match_Info { + my ( $self, $query, $property ) = @_; + return defined $property && defined $query->{Info} + && $query->{Info} =~ m/$property/; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End Processlist package +# ########################################################################### + +# ########################################################################### +# TcpdumpParser package 5155 +# ########################################################################### +package TcpdumpParser; + + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; + +use constant MKDEBUG => $ENV{MKDEBUG}; + +sub new { + my ( $class, %args ) = @_; + my $self = {}; + return bless $self, $class; +} + +sub parse_event { + my ( $self, %args ) = @_; + my @required_args = qw(fh); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my $fh = @args{@required_args}; + + if ( !$fh ) { + MKDEBUG && _d('No filehandle'); + return; + } + + local $INPUT_RECORD_SEPARATOR = "\n20"; + + my $pos_in_log = tell($fh); + while ( defined(my $raw_packet = <$fh>) ) { + next if $raw_packet =~ m/^$/; # issue 564 + $pos_in_log -= 1 if $pos_in_log; + + $raw_packet =~ s/\n20\Z//; + $raw_packet = "20$raw_packet" unless $raw_packet =~ m/\A20/; + + my $packet = $self->_parse_packet($raw_packet); + $packet->{pos_in_log} = $pos_in_log; + $packet->{raw_packet} = $raw_packet; + + return $packet; + } + + $args{oktorun}->(0) if $args{oktorun}; + return; +} + +sub _parse_packet { + my ( $self, $packet ) = @_; + die "I need a packet" unless $packet; + + my ( $ts, $source, $dest ) = $packet =~ m/\A(\S+ \S+) IP .*?(\S+) > (\S+):/; + my ( $src_host, $src_port ) = $source =~ m/((?:\d+\.){3}\d+)\.(\w+)/; + my ( $dst_host, $dst_port ) = $dest =~ m/((?:\d+\.){3}\d+)\.(\w+)/; + + $src_port = $self->port_number($src_port); + $dst_port = $self->port_number($dst_port); + + my $hex = qr/[0-9a-f]/; + (my $data = join('', $packet =~ m/\s+0x$hex+:\s((?:\s$hex{2,4})+)/go)) =~ s/\s+//g; + + my $ip_hlen = hex(substr($data, 1, 1)); # Num of 32-bit words in header. + my $ip_plen = hex(substr($data, 4, 4)); # Num of BYTES in IPv4 datagram. + my $complete = length($data) == 2 * $ip_plen ? 1 : 0; + + my $tcp_hlen = hex(substr($data, ($ip_hlen + 3) * 8, 1)); + + my $seq = hex(substr($data, ($ip_hlen + 1) * 8, 8)); + my $ack = hex(substr($data, ($ip_hlen + 2) * 8, 8)); + + my $flags = hex(substr($data, (($ip_hlen + 3) * 8) + 2, 2)); + + $data = substr($data, ($ip_hlen + $tcp_hlen) * 8); + + my $pkt = { + ts => $ts, + seq => $seq, + ack => $ack, + fin => $flags & 0x01, + syn => $flags & 0x02, + rst => $flags & 0x04, + src_host => $src_host, + src_port => $src_port, + dst_host => $dst_host, + dst_port => $dst_port, + complete => $complete, + ip_hlen => $ip_hlen, + tcp_hlen => $tcp_hlen, + dgram_len => $ip_plen, + data_len => $ip_plen - (($ip_hlen + $tcp_hlen) * 4), + data => $data ? substr($data, 0, 8).(length $data > 8 ? '...' : '') + : '', + }; + MKDEBUG && _d('packet:', Dumper($pkt)); + $pkt->{data} = $data; + return $pkt; +} + +sub port_number { + my ( $self, $port ) = @_; + return unless $port; + return $port eq 'memcached' ? 11211 + : $port eq 'http' ? 80 + : $port eq 'mysql' ? 3306 + : $port; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End TcpdumpParser package +# ########################################################################### + +# ########################################################################### +# MySQLProtocolParser package 5178 +# ########################################################################### +package MySQLProtocolParser; + + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); + +eval { + require IO::Uncompress::Inflate; + IO::Uncompress::Inflate->import(qw(inflate $InflateError)); +}; + +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; +$Data::Dumper::Quotekeys = 0; + +require Exporter; +our @ISA = qw(Exporter); +our %EXPORT_TAGS = (); +our @EXPORT = (); +our @EXPORT_OK = qw( + parse_error_packet + parse_ok_packet + parse_server_handshake_packet + parse_client_handshake_packet + parse_com_packet + parse_flags +); + +use constant MKDEBUG => $ENV{MKDEBUG}; +use constant { + COM_SLEEP => '00', + COM_QUIT => '01', + COM_INIT_DB => '02', + COM_QUERY => '03', + COM_FIELD_LIST => '04', + COM_CREATE_DB => '05', + COM_DROP_DB => '06', + COM_REFRESH => '07', + COM_SHUTDOWN => '08', + COM_STATISTICS => '09', + COM_PROCESS_INFO => '0a', + COM_CONNECT => '0b', + COM_PROCESS_KILL => '0c', + COM_DEBUG => '0d', + COM_PING => '0e', + COM_TIME => '0f', + COM_DELAYED_INSERT => '10', + COM_CHANGE_USER => '11', + COM_BINLOG_DUMP => '12', + COM_TABLE_DUMP => '13', + COM_CONNECT_OUT => '14', + COM_REGISTER_SLAVE => '15', + COM_STMT_PREPARE => '16', + COM_STMT_EXECUTE => '17', + COM_STMT_SEND_LONG_DATA => '18', + COM_STMT_CLOSE => '19', + COM_STMT_RESET => '1a', + COM_SET_OPTION => '1b', + COM_STMT_FETCH => '1c', + SERVER_QUERY_NO_GOOD_INDEX_USED => 16, + SERVER_QUERY_NO_INDEX_USED => 32, +}; + +my %com_for = ( + '00' => 'COM_SLEEP', + '01' => 'COM_QUIT', + '02' => 'COM_INIT_DB', + '03' => 'COM_QUERY', + '04' => 'COM_FIELD_LIST', + '05' => 'COM_CREATE_DB', + '06' => 'COM_DROP_DB', + '07' => 'COM_REFRESH', + '08' => 'COM_SHUTDOWN', + '09' => 'COM_STATISTICS', + '0a' => 'COM_PROCESS_INFO', + '0b' => 'COM_CONNECT', + '0c' => 'COM_PROCESS_KILL', + '0d' => 'COM_DEBUG', + '0e' => 'COM_PING', + '0f' => 'COM_TIME', + '10' => 'COM_DELAYED_INSERT', + '11' => 'COM_CHANGE_USER', + '12' => 'COM_BINLOG_DUMP', + '13' => 'COM_TABLE_DUMP', + '14' => 'COM_CONNECT_OUT', + '15' => 'COM_REGISTER_SLAVE', + '16' => 'COM_STMT_PREPARE', + '17' => 'COM_STMT_EXECUTE', + '18' => 'COM_STMT_SEND_LONG_DATA', + '19' => 'COM_STMT_CLOSE', + '1a' => 'COM_STMT_RESET', + '1b' => 'COM_SET_OPTION', + '1c' => 'COM_STMT_FETCH', +); + +my %flag_for = ( + 'CLIENT_LONG_PASSWORD' => 1, # new more secure passwords + 'CLIENT_FOUND_ROWS' => 2, # Found instead of affected rows + 'CLIENT_LONG_FLAG' => 4, # Get all column flags + 'CLIENT_CONNECT_WITH_DB' => 8, # One can specify db on connect + 'CLIENT_NO_SCHEMA' => 16, # Don't allow database.table.column + 'CLIENT_COMPRESS' => 32, # Can use compression protocol + 'CLIENT_ODBC' => 64, # Odbc client + 'CLIENT_LOCAL_FILES' => 128, # Can use LOAD DATA LOCAL + 'CLIENT_IGNORE_SPACE' => 256, # Ignore spaces before '(' + 'CLIENT_PROTOCOL_41' => 512, # New 4.1 protocol + 'CLIENT_INTERACTIVE' => 1024, # This is an interactive client + 'CLIENT_SSL' => 2048, # Switch to SSL after handshake + 'CLIENT_IGNORE_SIGPIPE' => 4096, # IGNORE sigpipes + 'CLIENT_TRANSACTIONS' => 8192, # Client knows about transactions + 'CLIENT_RESERVED' => 16384, # Old flag for 4.1 protocol + 'CLIENT_SECURE_CONNECTION' => 32768, # New 4.1 authentication + 'CLIENT_MULTI_STATEMENTS' => 65536, # Enable/disable multi-stmt support + 'CLIENT_MULTI_RESULTS' => 131072, # Enable/disable multi-results +); + +sub new { + my ( $class, %args ) = @_; + + my ( $server_port ) + = $args{server} ? $args{server} =~ m/:(\w+)/ : ('3306|mysql'); + $server_port ||= '3306|mysql'; # In case $args{server} doesn't have a port. + + my $self = { + server => $args{server}, + server_port => $server_port, + version => '41', # MySQL proto version; not used yet + sessions => {}, + o => $args{o}, + fake_thread_id => 2**32, # see _make_event() + }; + MKDEBUG && $self->{server} && _d('Watching only server', $self->{server}); + return bless $self, $class; +} + +sub parse_event { + my ( $self, %args ) = @_; + my @required_args = qw(event); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my $packet = @args{@required_args}; + + my $src_host = "$packet->{src_host}:$packet->{src_port}"; + my $dst_host = "$packet->{dst_host}:$packet->{dst_port}"; + + if ( my $server = $self->{server} ) { # Watch only the given server. + if ( $src_host ne $server && $dst_host ne $server ) { + MKDEBUG && _d('Packet is not to or from', $server); + return; + } + } + + my $packet_from; + my $client; + if ( $src_host =~ m/:$self->{server_port}$/ ) { + $packet_from = 'server'; + $client = $dst_host; + } + elsif ( $dst_host =~ m/:$self->{server_port}$/ ) { + $packet_from = 'client'; + $client = $src_host; + } + else { + MKDEBUG && _d('Packet is not to or from a MySQL server'); + return; + } + MKDEBUG && _d('Client:', $client); + + if ( !exists $self->{sessions}->{$client} ) { + MKDEBUG && _d('New session'); + $self->{sessions}->{$client} = { + client => $client, + ts => $packet->{ts}, + state => undef, + compress => undef, + raw_packets => [], + buff => '', + }; + }; + my $session = $self->{sessions}->{$client}; + + if ( $packet->{data_len} == 0 ) { + MKDEBUG && _d('No TCP/MySQL data'); + if ( ($session->{state} || '') eq 'closing' ) { + delete $self->{sessions}->{$session->{client}}; + MKDEBUG && _d('Session deleted'); + } + return; + } + + push @{$session->{raw_packets}}, $packet->{raw_packet}; + + if ( $session->{compress} ) { + return unless $self->uncompress_packet($packet, $session); + } + + if ( $session->{buff} && $packet_from eq 'client' ) { + $packet->{data} = $session->{buff} . $packet->{data}; + $session->{buff_left} -= $packet->{data_len}; + + $packet->{mysql_data_len} = $session->{mysql_data_len}; + + MKDEBUG && _d('Appending data to buff; expecting', + $session->{buff_left}, 'more bytes'); + } + else { + eval { + remove_mysql_header($packet); + }; + if ( $EVAL_ERROR ) { + MKDEBUG && _d('remove_mysql_header() failed; failing session'); + $session->{EVAL_ERROR} = $EVAL_ERROR; + $self->fail_session($session, 'remove_mysql_header() failed'); + return; + } + } + + my $event; + if ( $packet_from eq 'server' ) { + $event = $self->_packet_from_server($packet, $session, $args{misc}); + } + elsif ( $packet_from eq 'client' ) { + if ( $session->{buff} && $session->{buff_left} <= 0 ) { + MKDEBUG && _d('Data is complete'); + } + elsif ( $packet->{mysql_data_len} > $packet->{data_len} ) { + $session->{mysql_data_len} = $packet->{mysql_data_len}; + $session->{buff} = $packet->{data}; + + $session->{buff_left} + ||= $packet->{mysql_data_len} - $packet->{data_len}; + + MKDEBUG && _d('Data not complete; expecting', + $session->{buff_left}, 'more bytes'); + return; + } + $event = $self->_packet_from_client($packet, $session, $args{misc}); + + $session->{buff} = ''; + $session->{buff_left} = 0; + $session->{mysql_data_len} = 0; + } + else { + die 'Packet origin unknown'; + } + + MKDEBUG && _d('Done parsing packet; client state:', $session->{state}); + return $event; +} + +sub _packet_from_server { + my ( $self, $packet, $session, $misc ) = @_; + die "I need a packet" unless $packet; + die "I need a session" unless $session; + + MKDEBUG && _d('Packet is from server; client state:', $session->{state}); + + if ( ($session->{server_seq} || '') eq $packet->{seq} ) { + push @{ $session->{server_retransmissions} }, $packet->{seq}; + MKDEBUG && _d('TCP retransmission'); + return; + } + $session->{server_seq} = $packet->{seq}; + + my $data = $packet->{data}; + + + my ( $first_byte ) = substr($data, 0, 2, ''); + MKDEBUG && _d('First byte of packet:', $first_byte); + if ( !$first_byte ) { + $self->fail_session($session, 'no first byte'); + return; + } + + if ( !$session->{state} ) { + if ( $first_byte eq '0a' && length $data >= 33 && $data =~ m/00{13}/ ) { + my $handshake = parse_server_handshake_packet($data); + if ( !$handshake ) { + $self->fail_session($session, 'failed to parse server handshake'); + return; + } + $session->{state} = 'server_handshake'; + $session->{thread_id} = $handshake->{thread_id}; + } + else { + MKDEBUG && _d('Ignoring mid-stream server response'); + return; + } + } + else { + if ( $first_byte eq '00' ) { + if ( ($session->{state} || '') eq 'client_auth' ) { + + $session->{compress} = $session->{will_compress}; + delete $session->{will_compress}; + MKDEBUG && $session->{compress} && _d('Packets will be compressed'); + + MKDEBUG && _d('Admin command: Connect'); + return $self->_make_event( + { cmd => 'Admin', + arg => 'administrator command: Connect', + ts => $packet->{ts}, # Events are timestamped when they end + }, + $packet, $session + ); + } + elsif ( $session->{cmd} ) { + my $ok = parse_ok_packet($data); + if ( !$ok ) { + $self->fail_session($session, 'failed to parse OK packet'); + return; + } + my $com = $session->{cmd}->{cmd}; + my $arg; + + if ( $com eq COM_QUERY ) { + $com = 'Query'; + $arg = $session->{cmd}->{arg}; + } + else { + $arg = 'administrator command: ' + . ucfirst(lc(substr($com_for{$com}, 4))); + $com = 'Admin'; + } + + return $self->_make_event( + { cmd => $com, + arg => $arg, + ts => $packet->{ts}, + Insert_id => $ok->{insert_id}, + Warning_count => $ok->{warnings}, + Rows_affected => $ok->{affected_rows}, + }, + $packet, $session + ); + } + else { + MKDEBUG && _d('Looks like an OK packet but session has no cmd'); + } + } + elsif ( $first_byte eq 'ff' ) { + my $error = parse_error_packet($data); + if ( !$error ) { + $self->fail_session($session, 'failed to parse error packet'); + return; + } + my $event; + + if ( $session->{state} eq 'client_auth' ) { + MKDEBUG && _d('Connection failed'); + $event = { + cmd => 'Admin', + arg => 'administrator command: Connect', + ts => $packet->{ts}, + Error_no => $error->{errno} ? "#$error->{errno}" : 'none', + }; + return $self->_make_event($event, $packet, $session); + $session->{state} = 'closing'; + } + elsif ( $session->{cmd} ) { + my $com = $session->{cmd}->{cmd}; + my $arg; + + if ( $com eq COM_QUERY ) { + $com = 'Query'; + $arg = $session->{cmd}->{arg}; + } + else { + $arg = 'administrator command: ' + . ucfirst(lc(substr($com_for{$com}, 4))); + $com = 'Admin'; + } + + $event = { + cmd => $com, + arg => $arg, + ts => $packet->{ts}, + Error_no => $error->{errno} ? "#$error->{errno}" : 'none', + }; + return $self->_make_event($event, $packet, $session); + } + else { + MKDEBUG && _d('Looks like an error packet but client is not ' + . 'authenticating and session has no cmd'); + } + } + elsif ( $first_byte eq 'fe' && $packet->{mysql_data_len} < 9 ) { + if ( $packet->{mysql_data_len} == 1 + && $session->{state} eq 'client_auth' + && $packet->{number} == 2 ) + { + MKDEBUG && _d('Server has old password table;', + 'client will resend password using old algorithm'); + $session->{state} = 'client_auth_resend'; + } + else { + MKDEBUG && _d('Got an EOF packet'); + $self->fail_session($session, 'got an unexpected EOF packet'); + } + } + else { + if ( $session->{cmd} ) { + MKDEBUG && _d('Got a row/field/result packet'); + my $com = $session->{cmd}->{cmd}; + MKDEBUG && _d('Responding to client', $com_for{$com}); + my $event = { ts => $packet->{ts} }; + if ( $com eq COM_QUERY ) { + $event->{cmd} = 'Query'; + $event->{arg} = $session->{cmd}->{arg}; + } + else { + $event->{arg} = 'administrator command: ' + . ucfirst(lc(substr($com_for{$com}, 4))); + $event->{cmd} = 'Admin'; + } + + if ( $packet->{complete} ) { + my ( $warning_count, $status_flags ) + = $data =~ m/fe(.{4})(.{4})\Z/; + if ( $warning_count ) { + $event->{Warnings} = to_num($warning_count); + my $flags = to_num($status_flags); # TODO set all flags? + $event->{No_good_index_used} + = $flags & SERVER_QUERY_NO_GOOD_INDEX_USED ? 1 : 0; + $event->{No_index_used} + = $flags & SERVER_QUERY_NO_INDEX_USED ? 1 : 0; + } + } + + return $self->_make_event($event, $packet, $session); + } + else { + MKDEBUG && _d('Unknown in-stream server response'); + } + } + } + + return; +} + +sub _packet_from_client { + my ( $self, $packet, $session, $misc ) = @_; + die "I need a packet" unless $packet; + die "I need a session" unless $session; + + MKDEBUG && _d('Packet is from client; state:', $session->{state}); + + if ( ($session->{client_seq} || '') eq $packet->{seq} ) { + push @{ $session->{client_retransmissions} }, $packet->{seq}; + MKDEBUG && _d('TCP retransmission'); + return; + } + $session->{client_seq} = $packet->{seq}; + + my $data = $packet->{data}; + my $ts = $packet->{ts}; + + if ( ($session->{state} || '') eq 'server_handshake' ) { + MKDEBUG && _d('Expecting client authentication packet'); + my $handshake = parse_client_handshake_packet($data); + if ( !$handshake ) { + $self->fail_session($session, 'failed to parse client handshake'); + return; + } + $session->{state} = 'client_auth'; + $session->{pos_in_log} = $packet->{pos_in_log}; + $session->{user} = $handshake->{user}; + $session->{db} = $handshake->{db}; + + $session->{will_compress} = $handshake->{flags}->{CLIENT_COMPRESS}; + } + elsif ( ($session->{state} || '') eq 'client_auth_resend' ) { + MKDEBUG && _d('Client resending password using old algorithm'); + $session->{state} = 'client_auth'; + } + elsif ( ($session->{state} || '') eq 'awaiting_reply' ) { + my $arg = $session->{cmd}->{arg} ? substr($session->{cmd}->{arg}, 0, 50) + : 'unknown'; + MKDEBUG && _d('More data for previous command:', $arg, '...'); + return; + } + else { + + if ( !defined $session->{compress} ) { + return unless $self->detect_compression($packet, $session); + $data = $packet->{data}; + } + + my $com = parse_com_packet($data, $packet->{mysql_data_len}); + if ( !$com ) { + $self->fail_session($session, 'failed to parse COM packet'); + return; + } + $session->{state} = 'awaiting_reply'; + $session->{pos_in_log} = $packet->{pos_in_log}; + $session->{ts} = $ts; + $session->{cmd} = { + cmd => $com->{code}, + arg => $com->{data}, + }; + + if ( $com->{code} eq COM_QUIT ) { # Fire right away; will cleanup later. + MKDEBUG && _d('Got a COM_QUIT'); + return $self->_make_event( + { cmd => 'Admin', + arg => 'administrator command: Quit', + ts => $ts, + }, + $packet, $session + ); + $session->{state} = 'closing'; + } + } + + return; +} + +sub _make_event { + my ( $self, $event, $packet, $session ) = @_; + MKDEBUG && _d('Making event'); + + $session->{raw_packets} = []; + + if ( !$session->{thread_id} ) { + MKDEBUG && _d('Giving session fake thread id', $self->{fake_thread_id}); + $session->{thread_id} = $self->{fake_thread_id}++; + } + + my ($host, $port) = $session->{client} =~ m/((?:\d+\.){3}\d+)\:(\w+)/; + my $new_event = { + cmd => $event->{cmd}, + arg => $event->{arg}, + bytes => length( $event->{arg} ), + ts => tcp_timestamp( $event->{ts} ), + host => $host, + ip => $host, + port => $port, + db => $session->{db}, + user => $session->{user}, + Thread_id => $session->{thread_id}, + pos_in_log => $session->{pos_in_log}, + Query_time => timestamp_diff($session->{ts}, $packet->{ts}), + Error_no => $event->{Error_no} || 'none', + Rows_affected => ($event->{Rows_affected} || 0), + Warning_count => ($event->{Warning_count} || 0), + No_good_index_used => ($event->{No_good_index_used} ? 'Yes' : 'No'), + No_index_used => ($event->{No_index_used} ? 'Yes' : 'No'), + }; + MKDEBUG && _d('Properties of event:', Dumper($new_event)); + + delete $session->{cmd}; + + $session->{state} = undef; + + return $new_event; +} + +sub tcp_timestamp { + my ( $ts ) = @_; + $ts =~ s/^\d\d(\d\d)-(\d\d)-(\d\d)/$1$2$3/; + return $ts; +} + +sub timestamp_diff { + my ( $start, $end ) = @_; + my $sd = substr($start, 0, 11, ''); + my $ed = substr($end, 0, 11, ''); + my ( $sh, $sm, $ss ) = split(/:/, $start); + my ( $eh, $em, $es ) = split(/:/, $end); + my $esecs = ($eh * 3600 + $em * 60 + $es); + my $ssecs = ($sh * 3600 + $sm * 60 + $ss); + if ( $sd eq $ed ) { + return sprintf '%.6f', $esecs - $ssecs; + } + else { # Assume only one day boundary has been crossed, no DST, etc + return sprintf '%.6f', ( 86_400 - $ssecs ) + $esecs; + } +} + +sub to_string { + my ( $data ) = @_; + $data = pack('H*', $data); + return $data; +} + +sub to_num { + my ( $str ) = @_; + my @bytes = $str =~ m/(..)/g; + my $result = 0; + foreach my $i ( 0 .. $#bytes ) { + $result += hex($bytes[$i]) * (16 ** ($i * 2)); + } + return $result; +} + +sub get_lcb { + my ( $string ) = @_; + my $first_byte = hex(substr($$string, 0, 2, '')); + if ( $first_byte < 251 ) { + return $first_byte; + } + elsif ( $first_byte == 252 ) { + return to_num(substr($$string, 0, 4, '')); + } + elsif ( $first_byte == 253 ) { + return to_num(substr($$string, 0, 6, '')); + } + elsif ( $first_byte == 254 ) { + return to_num(substr($$string, 0, 16, '')); + } +} + +sub parse_error_packet { + my ( $data ) = @_; + return unless $data; + MKDEBUG && _d('ERROR data:', $data); + if ( length $data < 16 ) { + MKDEBUG && _d('Error packet is too short:', $data); + return; + } + my $errno = to_num(substr($data, 0, 4)); + my $marker = to_string(substr($data, 4, 2)); + return unless $marker eq '#'; + my $sqlstate = to_string(substr($data, 6, 10)); + my $message = to_string(substr($data, 16)); + my $pkt = { + errno => $errno, + sqlstate => $marker . $sqlstate, + message => $message, + }; + MKDEBUG && _d('Error packet:', Dumper($pkt)); + return $pkt; +} + +sub parse_ok_packet { + my ( $data ) = @_; + return unless $data; + MKDEBUG && _d('OK data:', $data); + if ( length $data < 12 ) { + MKDEBUG && _d('OK packet is too short:', $data); + return; + } + my $affected_rows = get_lcb(\$data); + my $insert_id = get_lcb(\$data); + my $status = to_num(substr($data, 0, 4, '')); + my $warnings = to_num(substr($data, 0, 4, '')); + my $message = to_string($data); + my $pkt = { + affected_rows => $affected_rows, + insert_id => $insert_id, + status => $status, + warnings => $warnings, + message => $message, + }; + MKDEBUG && _d('OK packet:', Dumper($pkt)); + return $pkt; +} + +sub parse_server_handshake_packet { + my ( $data ) = @_; + return unless $data; + MKDEBUG && _d('Server handshake data:', $data); + my $handshake_pattern = qr{ + ^ # ----- ---- + (.+?)00 # n Null-Term String server_version + (.{8}) # 4 thread_id + .{16} # 8 scramble_buff + .{2} # 1 filler: always 0x00 + (.{4}) # 2 server_capabilities + .{2} # 1 server_language + .{4} # 2 server_status + .{26} # 13 filler: always 0x00 + }x; + my ( $server_version, $thread_id, $flags ) = $data =~ m/$handshake_pattern/; + my $pkt = { + server_version => to_string($server_version), + thread_id => to_num($thread_id), + flags => parse_flags($flags), + }; + MKDEBUG && _d('Server handshake packet:', Dumper($pkt)); + return $pkt; +} + +sub parse_client_handshake_packet { + my ( $data ) = @_; + return unless $data; + MKDEBUG && _d('Client handshake data:', $data); + my ( $flags, $user, $buff_len ) = $data =~ m{ + ^ + (.{8}) # Client flags + .{10} # Max packet size, charset + (?:00){23} # Filler + ((?:..)+?)00 # Null-terminated user name + (..) # Length-coding byte for scramble buff + }x; + + if ( !$buff_len ) { + MKDEBUG && _d('Did not match client handshake packet'); + return; + } + + my $code_len = hex($buff_len); + my ( $db ) = $data =~ m! + ^.{64}${user}00.. # Everything matched before + (?:..){$code_len} # The scramble buffer + (.*)00\Z # The database name + !x; + my $pkt = { + user => to_string($user), + db => $db ? to_string($db) : '', + flags => parse_flags($flags), + }; + MKDEBUG && _d('Client handshake packet:', Dumper($pkt)); + return $pkt; +} + +sub parse_com_packet { + my ( $data, $len ) = @_; + return unless $data && $len; + MKDEBUG && _d('COM data:', + (substr($data, 0, 100).(length $data > 100 ? '...' : '')), + 'len:', $len); + my $code = substr($data, 0, 2); + my $com = $com_for{$code}; + if ( !$com ) { + MKDEBUG && _d('Did not match COM packet'); + return; + } + $data = to_string(substr($data, 2, ($len - 1) * 2)); + my $pkt = { + code => $code, + com => $com, + data => $data, + }; + MKDEBUG && _d('COM packet:', Dumper($pkt)); + return $pkt; +} + +sub parse_flags { + my ( $flags ) = @_; + die "I need flags" unless $flags; + MKDEBUG && _d('Flag data:', $flags); + my %flags = %flag_for; + my $flags_dec = to_num($flags); + foreach my $flag ( keys %flag_for ) { + my $flagno = $flag_for{$flag}; + $flags{$flag} = ($flags_dec & $flagno ? 1 : 0); + } + return \%flags; +} + +sub uncompress_data { + my ( $data, $len ) = @_; + die "I need data" unless $data; + die "I need a len argument" unless $len; + die "I need a scalar reference to data" unless ref $data eq 'SCALAR'; + MKDEBUG && _d('Uncompressing data'); + our $InflateError; + + my $comp_bin_data = pack('H*', $$data); + + my $uncomp_bin_data = ''; + my $z = new IO::Uncompress::Inflate( + \$comp_bin_data + ) or die "IO::Uncompress::Inflate failed: $InflateError"; + my $status = $z->read(\$uncomp_bin_data, $len) + or die "IO::Uncompress::Inflate failed: $InflateError"; + + my $uncomp_data = unpack('H*', $uncomp_bin_data); + + return \$uncomp_data; +} + +sub detect_compression { + my ( $self, $packet, $session ) = @_; + MKDEBUG && _d('Checking for client compression'); + my $com = parse_com_packet($packet->{data}, $packet->{data_len}); + if ( $com && $com->{code} eq COM_SLEEP ) { + MKDEBUG && _d('Client is using compression'); + $session->{compress} = 1; + + $packet->{data} = $packet->{mysql_hdr} . $packet->{data}; + return 0 unless $self->uncompress_packet($packet, $session); + remove_mysql_header($packet); + } + else { + MKDEBUG && _d('Client is NOT using compression'); + $session->{compress} = 0; + } + return 1; +} + +sub uncompress_packet { + my ( $self, $packet, $session ) = @_; + die "I need a packet" unless $packet; + die "I need a session" unless $session; + + + my $data; + my $comp_hdr; + my $comp_data_len; + my $pkt_num; + my $uncomp_data_len; + eval { + $data = \$packet->{data}; + $comp_hdr = substr($$data, 0, 14, ''); + $comp_data_len = to_num(substr($comp_hdr, 0, 6)); + $pkt_num = to_num(substr($comp_hdr, 6, 2)); + $uncomp_data_len = to_num(substr($comp_hdr, 8, 6)); + MKDEBUG && _d('Compression header data:', $comp_hdr, + 'compressed data len (bytes)', $comp_data_len, + 'number', $pkt_num, + 'uncompressed data len (bytes)', $uncomp_data_len); + }; + if ( $EVAL_ERROR ) { + $session->{EVAL_ERROR} = $EVAL_ERROR; + $self->fail_session($session, 'failed to parse compression header'); + return 0; + } + + if ( $uncomp_data_len ) { + eval { + $data = uncompress_data($data, $uncomp_data_len); + $packet->{data} = $$data; + }; + if ( $EVAL_ERROR ) { + $session->{EVAL_ERROR} = $EVAL_ERROR; + $self->fail_session($session, 'failed to uncompress data'); + die "Cannot uncompress packet. Check that IO::Uncompress::Inflate " + . "is installed.\nError: $EVAL_ERROR"; + } + } + else { + MKDEBUG && _d('Packet is not really compressed'); + $packet->{data} = $$data; + } + + return 1; +} + +sub remove_mysql_header { + my ( $packet ) = @_; + die "I need a packet" unless $packet; + + my $mysql_hdr = substr($packet->{data}, 0, 8, ''); + my $mysql_data_len = to_num(substr($mysql_hdr, 0, 6)); + my $pkt_num = to_num(substr($mysql_hdr, 6, 2)); + MKDEBUG && _d('MySQL packet: header data', $mysql_hdr, + 'data len (bytes)', $mysql_data_len, 'number', $pkt_num); + + $packet->{mysql_hdr} = $mysql_hdr; + $packet->{mysql_data_len} = $mysql_data_len; + $packet->{number} = $pkt_num; + + return; +} + +sub _get_errors_fh { + my ( $self ) = @_; + my $errors_fh = $self->{errors_fh}; + return $errors_fh if $errors_fh; + + my $o = $self->{o}; + if ( $o && $o->has('tcpdump-errors') && $o->got('tcpdump-errors') ) { + my $errors_file = $o->get('tcpdump-errors'); + MKDEBUG && _d('tcpdump-errors file:', $errors_file); + open $errors_fh, '>>', $errors_file + or die "Cannot open tcpdump-errors file $errors_file: $OS_ERROR"; + } + + $self->{errors_fh} = $errors_fh; + return $errors_fh; +} + +sub fail_session { + my ( $self, $session, $reason ) = @_; + my $errors_fh = $self->_get_errors_fh(); + if ( $errors_fh ) { + my $raw_packets = $session->{raw_packets}; + delete $session->{raw_packets}; # Don't dump, it's printed below. + $session->{reason_for_failure} = $reason; + my $session_dump = '# ' . Dumper($session); + chomp $session_dump; + $session_dump =~ s/\n/\n# /g; + print $errors_fh "$session_dump\n"; + { + local $LIST_SEPARATOR = "\n"; + print $errors_fh "@$raw_packets"; + print $errors_fh "\n"; + } + } + MKDEBUG && _d('Failed session', $session->{client}, 'because', $reason); + delete $self->{sessions}->{$session->{client}}; + return; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End MySQLProtocolParser package +# ########################################################################### + +# ########################################################################### +# SlowLogParser package 5133 +# ########################################################################### +package SlowLogParser; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); +use Data::Dumper; + +use constant MKDEBUG => $ENV{MKDEBUG}; + +sub new { + my ( $class ) = @_; + my $self = { + pending => [], + }; + return bless $self, $class; +} + +my $slow_log_ts_line = qr/^# Time: ([0-9: ]{15})/; +my $slow_log_uh_line = qr/# User\@Host: ([^\[]+|\[[^[]+\]).*?@ (\S*) \[(.*)\]/; +my $slow_log_hd_line = qr{ + ^(?: + T[cC][pP]\s[pP]ort:\s+\d+ # case differs on windows/unix + | + [/A-Z].*mysqld,\sVersion.*(?:started\swith:|embedded\slibrary) + | + Time\s+Id\s+Command + ).*\n + }xm; + +sub parse_event { + my ( $self, %args ) = @_; + my @required_args = qw(fh); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my $fh = @args{@required_args}; + + my $pending = $self->{pending}; + local $INPUT_RECORD_SEPARATOR = ";\n#"; + my $trimlen = length($INPUT_RECORD_SEPARATOR); + my $pos_in_log = tell($fh); + my $stmt; + + EVENT: + while ( (defined($stmt = shift @$pending) or defined($stmt = <$fh>)) ) { + my @properties = ('cmd', 'Query', 'pos_in_log', $pos_in_log); + $pos_in_log = tell($fh); + + if ( $stmt =~ s/$slow_log_hd_line//go ){ # Throw away header lines in log + my @chunks = split(/$INPUT_RECORD_SEPARATOR/o, $stmt); + if ( @chunks > 1 ) { + MKDEBUG && _d("Found multiple chunks"); + $stmt = shift @chunks; + unshift @$pending, @chunks; + } + } + + $stmt = '#' . $stmt unless $stmt =~ m/\A#/; + $stmt =~ s/;\n#?\Z//; + + + my ($got_ts, $got_uh, $got_ac, $got_db, $got_set, $got_embed); + my $pos = 0; + my $len = length($stmt); + my $found_arg = 0; + LINE: + while ( $stmt =~ m/^(.*)$/mg ) { # /g is important, requires scalar match. + $pos = pos($stmt); # Be careful not to mess this up! + my $line = $1; # Necessary for /g and pos() to work. + MKDEBUG && _d($line); + + if ($line =~ m/^(?:#|use |SET (?:last_insert_id|insert_id|timestamp))/o) { + + if ( !$got_ts && (my ( $time ) = $line =~ m/$slow_log_ts_line/o)) { + MKDEBUG && _d("Got ts", $time); + push @properties, 'ts', $time; + ++$got_ts; + if ( !$got_uh + && ( my ( $user, $host, $ip ) = $line =~ m/$slow_log_uh_line/o ) + ) { + MKDEBUG && _d("Got user, host, ip", $user, $host, $ip); + push @properties, 'user', $user, 'host', $host, 'ip', $ip; + ++$got_uh; + } + } + + elsif ( !$got_uh + && ( my ( $user, $host, $ip ) = $line =~ m/$slow_log_uh_line/o ) + ) { + MKDEBUG && _d("Got user, host, ip", $user, $host, $ip); + push @properties, 'user', $user, 'host', $host, 'ip', $ip; + ++$got_uh; + } + + elsif (!$got_ac && $line =~ m/^# (?:administrator command:.*)$/) { + MKDEBUG && _d("Got admin command"); + push @properties, 'cmd', 'Admin', 'arg', $line; + push @properties, 'bytes', length($properties[-1]); + ++$found_arg; + ++$got_ac; + } + + elsif ( $line =~ m/^# +[A-Z][A-Za-z_]+: \S+/ ) { # Make the test cheap! + MKDEBUG && _d("Got some line with properties"); + my @temp = $line =~ m/(\w+):\s+(\S+|\Z)/g; + push @properties, @temp; + } + + elsif ( !$got_db && (my ( $db ) = $line =~ m/^use ([^;]+)/ ) ) { + MKDEBUG && _d("Got a default database:", $db); + push @properties, 'db', $db; + ++$got_db; + } + + elsif (!$got_set && (my ($setting) = $line =~ m/^SET\s+([^;]*)/)) { + MKDEBUG && _d("Got some setting:", $setting); + push @properties, split(/,|\s*=\s*/, $setting); + ++$got_set; + } + + if ( !$found_arg && $pos == $len ) { + MKDEBUG && _d("Did not find arg, looking for special cases"); + local $INPUT_RECORD_SEPARATOR = ";\n"; + if ( defined(my $l = <$fh>) ) { + chomp $l; + MKDEBUG && _d("Found admin statement", $l); + push @properties, 'cmd', 'Admin', 'arg', '#' . $l; + push @properties, 'bytes', length($properties[-1]); + $found_arg++; + } + else { + MKDEBUG && _d("I can't figure out what to do with this line"); + next EVENT; + } + } + } + else { + MKDEBUG && _d("Got the query/arg line"); + my $arg = substr($stmt, $pos - length($line)); + push @properties, 'arg', $arg, 'bytes', length($arg); + if ( $args{misc} && $args{misc}->{embed} + && ( my ($e) = $arg =~ m/($args{misc}->{embed})/) + ) { + push @properties, $e =~ m/$args{misc}->{capture}/g; + } + last LINE; + } + } + + MKDEBUG && _d('Properties of event:', Dumper(\@properties)); + my $event = { @properties }; + return $event; + } + + @$pending = (); + $args{oktorun}->(0) if $args{oktorun}; + return; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End SlowLogParser package +# ########################################################################### + +# ########################################################################### +# SlowLogWriter package 5191 +# ########################################################################### +package SlowLogWriter; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); + +use constant MKDEBUG => $ENV{MKDEBUG}; + +sub new { + my ( $class ) = @_; + bless {}, $class; +} + +sub write { + my ( $self, $fh, $event ) = @_; + if ( $event->{ts} ) { + print $fh "# Time: $event->{ts}\n"; + } + if ( $event->{user} ) { + printf $fh "# User\@Host: %s[%s] \@ %s []\n", + $event->{user}, $event->{user}, $event->{host}; + } + if ( $event->{ip} && $event->{port} ) { + printf $fh "# Client: $event->{ip}:$event->{port}\n"; + } + if ( $event->{Thread_id} ) { + printf $fh "# Thread_id: $event->{Thread_id}\n"; + } + + my $percona_patched = exists $event->{QC_Hit} ? 1 : 0; + + printf $fh + "# Query_time: %.6f Lock_time: %.6f Rows_sent: %d Rows_examined: %d\n", + map { $_ || 0 } + @{$event}{qw(Query_time Lock_time Rows_sent Rows_examined)}; + + if ( $percona_patched ) { + printf $fh + "# QC_Hit: %s Full_scan: %s Full_join: %s Tmp_table: %s Disk_tmp_table: %s\n# Filesort: %s Disk_filesort: %s Merge_passes: %d\n", + map { $_ || 0 } + @{$event}{qw(QC_Hit Full_scan Full_join Tmp_table Disk_tmp_table Filesort Disk_filesort Merge_passes)}; + + if ( exists $event->{InnoDB_IO_r_ops} ) { + printf $fh + "# InnoDB_IO_r_ops: %d InnoDB_IO_r_bytes: %d InnoDB_IO_r_wait: %s\n# InnoDB_rec_lock_wait: %s InnoDB_queue_wait: %s\n# InnoDB_pages_distinct: %d\n", + map { $_ || 0 } + @{$event}{qw(InnoDB_IO_r_ops InnoDB_IO_r_bytes InnoDB_IO_r_wait InnoDB_rec_lock_wait InnoDB_queue_wait InnoDB_pages_distinct)}; + + } + else { + printf $fh "# No InnoDB statistics available for this query\n"; + } + } + + if ( $event->{db} ) { + printf $fh "use %s;\n", $event->{db}; + } + if ( $event->{arg} =~ m/^administrator command/ ) { + print $fh '# '; + } + print $fh $event->{arg}, ";\n"; + + return; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End SlowLogWriter package +# ########################################################################### + +# ########################################################################### +# EventAggregator package 4916 +# ########################################################################### +package EventAggregator; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); + +use constant MKDEBUG => $ENV{MKDEBUG}; +use constant BUCK_SIZE => 1.05; +use constant BASE_LOG => log(BUCK_SIZE); +use constant BASE_OFFSET => abs(1 - log(0.000001) / BASE_LOG); # 284.1617969 +use constant NUM_BUCK => 1000; +use constant MIN_BUCK => .000001; + +our @buckets = map { 0 } (0..NUM_BUCK-1); + +my @buck_vals = map { bucket_value($_); } (0..NUM_BUCK-1); + +sub new { + my ( $class, %args ) = @_; + foreach my $arg ( qw(groupby worst) ) { + die "I need a $arg argument" unless $args{$arg}; + } + my $attributes = $args{attributes} || {}; + my $self = { + groupby => $args{groupby}, + detect_attribs => scalar keys %$attributes == 0 ? 1 : 0, + all_attribs => [ keys %$attributes ], + ignore_attribs => { + map { $_ => $args{attributes}->{$_} } + grep { $_ ne $args{groupby} } + @{$args{ignore_attributes}} + }, + attributes => { + map { $_ => $args{attributes}->{$_} } + grep { $_ ne $args{groupby} } + keys %$attributes + }, + alt_attribs => { + map { $_ => make_alt_attrib(@{$args{attributes}->{$_}}) } + grep { $_ ne $args{groupby} } + keys %$attributes + }, + worst => $args{worst}, + unroll_limit => $args{unroll_limit} || 1000, + attrib_limit => $args{attrib_limit}, + result_classes => {}, + result_globals => {}, + result_samples => {}, + n_events => 0, + unrolled_loops => undef, + type_for => { %{$args{type_for} || { Query_time => 'num' }} }, + }; + return bless $self, $class; +} + +sub reset_aggregated_data { + my ( $self ) = @_; + foreach my $class ( values %{$self->{result_classes}} ) { + foreach my $attrib ( values %$class ) { + delete @{$attrib}{keys %$attrib}; + } + } + foreach my $class ( values %{$self->{result_globals}} ) { + delete @{$class}{keys %$class}; + } + delete @{$self->{result_samples}}{keys %{$self->{result_samples}}}; + $self->{n_events} = 0; +} + +sub aggregate { + my ( $self, $event ) = @_; + + my $group_by = $event->{$self->{groupby}}; + return unless defined $group_by; + + $self->{n_events}++; + MKDEBUG && _d('event', $self->{n_events}); + + return $self->{unrolled_loops}->($self, $event, $group_by) + if $self->{unrolled_loops}; + + if ( $self->{n_events} <= $self->{unroll_limit} ) { + + $self->add_new_attributes($event) if $self->{detect_attribs}; + + ATTRIB: + foreach my $attrib ( keys %{$self->{attributes}} ) { + + if ( !exists $event->{$attrib} ) { + MKDEBUG && _d("attrib doesn't exist in event:", $attrib); + my $alt_attrib = $self->{alt_attribs}->{$attrib}->($event); + MKDEBUG && _d('alt attrib:', $alt_attrib); + next ATTRIB unless $alt_attrib; + } + + GROUPBY: + foreach my $val ( ref $group_by ? @$group_by : ($group_by) ) { + my $class_attrib = $self->{result_classes}->{$val}->{$attrib} ||= {}; + my $global_attrib = $self->{result_globals}->{$attrib} ||= {}; + my $samples = $self->{result_samples}; + my $handler = $self->{handlers}->{ $attrib }; + if ( !$handler ) { + $handler = $self->make_handler( + $attrib, + $event, + wor => $self->{worst} eq $attrib, + alt => $self->{attributes}->{$attrib}, + ); + $self->{handlers}->{$attrib} = $handler; + } + next GROUPBY unless $handler; + $samples->{$val} ||= $event; # Initialize to the first event. + $handler->($event, $class_attrib, $global_attrib, $samples, $group_by); + } + } + } + else { + $self->_make_unrolled_loops($event); + $self->{unrolled_loops}->($self, $event, $group_by); + } + + return; +} + +sub _make_unrolled_loops { + my ( $self, $event ) = @_; + + my $group_by = $event->{$self->{groupby}}; + + my @attrs = grep { $self->{handlers}->{$_} } keys %{$self->{attributes}}; + my $globs = $self->{result_globals}; # Global stats for each + my $samples = $self->{result_samples}; + + my @lines = ( + 'my ( $self, $event, $group_by ) = @_;', + 'my ($val, $class, $global, $idx);', + (ref $group_by ? ('foreach my $group_by ( @$group_by ) {') : ()), + 'my $temp = $self->{result_classes}->{ $group_by } + ||= { map { $_ => { } } @attrs };', + '$samples->{$group_by} ||= $event;', # Always start with the first. + ); + foreach my $i ( 0 .. $#attrs ) { + push @lines, ( + '$class = $temp->{\'' . $attrs[$i] . '\'};', + '$global = $globs->{\'' . $attrs[$i] . '\'};', + $self->{unrolled_for}->{$attrs[$i]}, + ); + } + if ( ref $group_by ) { + push @lines, '}'; # Close the loop opened above + } + @lines = map { s/^/ /gm; $_ } @lines; # Indent for debugging + unshift @lines, 'sub {'; + push @lines, '}'; + + my $code = join("\n", @lines); + MKDEBUG && _d('Unrolled subroutine:', @lines); + my $sub = eval $code; + die $EVAL_ERROR if $EVAL_ERROR; + $self->{unrolled_loops} = $sub; + + return; +} + +sub results { + my ( $self ) = @_; + return { + classes => $self->{result_classes}, + globals => $self->{result_globals}, + samples => $self->{result_samples}, + }; +} + +sub attributes { + my ( $self ) = @_; + return $self->{type_for}; +} + +sub type_for { + my ( $self, $attrib ) = @_; + return $self->{type_for}->{$attrib}; +} + +sub make_handler { + my ( $self, $attrib, $event, %args ) = @_; + die "I need an attrib" unless defined $attrib; + my ($val) = grep { defined $_ } map { $event->{$_} } @{ $args{alt} }; + my $is_array = 0; + if (ref $val eq 'ARRAY') { + $is_array = 1; + $val = $val->[0]; + } + return unless defined $val; # Can't decide type if it's undef. + + my $float_re = qr{[+-]?(?:(?=\d|[.])\d+(?:[.])\d{0,})(?:E[+-]?\d+)?}i; + my $type = $self->type_for($attrib) ? $self->type_for($attrib) + : $val =~ m/^(?:\d+|$float_re)$/o ? 'num' + : $val =~ m/^(?:Yes|No)$/ ? 'bool' + : 'string'; + MKDEBUG && _d('Type for', $attrib, 'is', $type, + '(sample:', $val, '), is array:', $is_array); + $self->{type_for}->{$attrib} = $type; + + %args = ( # Set up defaults + min => 1, + max => 1, + sum => $type =~ m/num|bool/ ? 1 : 0, + cnt => 1, + unq => $type =~ m/bool|string/ ? 1 : 0, + all => $type eq 'num' ? 1 : 0, + glo => 1, + trf => ($type eq 'bool') ? q{(($val || '') eq 'Yes') ? 1 : 0} : undef, + wor => 0, + alt => [], + %args, + ); + + my @lines = ("# type: $type"); # Lines of code for the subroutine + if ( $args{trf} ) { + push @lines, q{$val = } . $args{trf} . ';'; + } + + foreach my $place ( qw($class $global) ) { + my @tmp; + if ( $args{min} ) { + my $op = $type eq 'num' ? '<' : 'lt'; + push @tmp, ( + 'PLACE->{min} = $val if !defined PLACE->{min} || $val ' + . $op . ' PLACE->{min};', + ); + } + if ( $args{max} ) { + my $op = ($type eq 'num') ? '>' : 'gt'; + push @tmp, ( + 'PLACE->{max} = $val if !defined PLACE->{max} || $val ' + . $op . ' PLACE->{max};', + ); + } + if ( $args{sum} ) { + push @tmp, 'PLACE->{sum} += $val;'; + } + if ( $args{cnt} ) { + push @tmp, '++PLACE->{cnt};'; + } + if ( $args{all} ) { + push @tmp, ( + 'exists PLACE->{all} or PLACE->{all} = [ @buckets ];', + '++PLACE->{all}->[ EventAggregator::bucket_idx($val) ];', + ); + } + push @lines, map { s/PLACE/$place/g; $_ } @tmp; + } + + if ( $args{unq} ) { + push @lines, '++$class->{unq}->{$val};'; + } + if ( $args{wor} ) { + my $op = $type eq 'num' ? '>=' : 'ge'; + push @lines, ( + 'if ( $val ' . $op . ' ($class->{max} || 0) ) {', + ' $samples->{$group_by} = $event;', + '}', + ); + } + + my @broken_query_time; + if ( $attrib eq 'Query_time' ) { + push @broken_query_time, ( + '$val =~ s/^(\d+(?:\.\d+)?).*/$1/;', + '$event->{\''.$attrib.'\'} = $val;', + ); + } + + my @limit; + if ( $args{all} && $type eq 'num' && $self->{attrib_limit} ) { + push @limit, ( + "if ( \$val > $self->{attrib_limit} ) {", + ' $val = $class->{last} ||= 0;', + '}', + '$class->{last} = $val;', + ); + } + + my @unrolled = ( + "\$val = \$event->{'$attrib'};", + ($is_array ? ('foreach my $val ( @$val ) {') : ()), + (map { "\$val = \$event->{'$_'} unless defined \$val;" } + grep { $_ ne $attrib } @{$args{alt}}), + 'defined $val && do {', + ( map { s/^/ /gm; $_ } (@broken_query_time, @limit, @lines) ), # Indent for debugging + '};', + ($is_array ? ('}') : ()), + ); + $self->{unrolled_for}->{$attrib} = join("\n", @unrolled); + + unshift @lines, ( + 'sub {', + 'my ( $event, $class, $global, $samples, $group_by ) = @_;', + 'my ($val, $idx);', # NOTE: define all variables here + "\$val = \$event->{'$attrib'};", + (map { "\$val = \$event->{'$_'} unless defined \$val;" } + grep { $_ ne $attrib } @{$args{alt}}), + 'return unless defined $val;', + ($is_array ? ('foreach my $val ( @$val ) {') : ()), + @broken_query_time, + @limit, + ($is_array ? ('}') : ()), + ); + push @lines, '}'; + my $code = join("\n", @lines); + $self->{code_for}->{$attrib} = $code; + + MKDEBUG && _d('Metric handler for', $attrib, ':', @lines); + my $sub = eval join("\n", @lines); + die if $EVAL_ERROR; + return $sub; +} + +sub bucket_idx { + my ( $val ) = @_; + return 0 if $val < MIN_BUCK; + my $idx = int(BASE_OFFSET + log($val)/BASE_LOG); + return $idx > (NUM_BUCK-1) ? (NUM_BUCK-1) : $idx; +} + +sub bucket_value { + my ( $bucket ) = @_; + return 0 if $bucket == 0; + die "Invalid bucket: $bucket" if $bucket < 0 || $bucket > (NUM_BUCK-1); + return (BUCK_SIZE**($bucket-1)) * MIN_BUCK; +} + +{ + my @buck_tens; + sub buckets_of { + return @buck_tens if @buck_tens; + + my $start_bucket = 0; + my @base10_starts = (0); + map { push @base10_starts, (10**$_)*MIN_BUCK } (1..7); + + for my $base10_bucket ( 0..($#base10_starts-1) ) { + my $next_bucket = bucket_idx( $base10_starts[$base10_bucket+1] ); + MKDEBUG && _d('Base 10 bucket', $base10_bucket, 'maps to', + 'base 1.05 buckets', $start_bucket, '..', $next_bucket-1); + for my $base1_05_bucket ($start_bucket..($next_bucket-1)) { + $buck_tens[$base1_05_bucket] = $base10_bucket; + } + $start_bucket = $next_bucket; + } + + map { $buck_tens[$_] = 7 } ($start_bucket..(NUM_BUCK-1)); + + return @buck_tens; + } +} + +sub calculate_statistical_metrics { + my ( $self, $vals, $args ) = @_; + my $statistical_metrics = { + pct_95 => 0, + stddev => 0, + median => 0, + cutoff => undef, + }; + + return $statistical_metrics + unless defined $vals && @$vals && $args->{cnt}; + + my $n_vals = $args->{cnt}; + if ( $n_vals == 1 || $args->{max} == $args->{min} ) { + my $v = $args->{max} || 0; + my $bucket = int(6 + ( log($v > 0 ? $v : MIN_BUCK) / log(10))); + $bucket = $bucket > 7 ? 7 : $bucket < 0 ? 0 : $bucket; + return { + pct_95 => $v, + stddev => 0, + median => $v, + cutoff => $n_vals, + }; + } + elsif ( $n_vals == 2 ) { + foreach my $v ( $args->{min}, $args->{max} ) { + my $bucket = int(6 + ( log($v && $v > 0 ? $v : MIN_BUCK) / log(10))); + $bucket = $bucket > 7 ? 7 : $bucket < 0 ? 0 : $bucket; + } + my $v = $args->{max} || 0; + my $mean = (($args->{min} || 0) + $v) / 2; + return { + pct_95 => $v, + stddev => sqrt((($v - $mean) ** 2) *2), + median => $mean, + cutoff => $n_vals, + }; + } + + my $cutoff = $n_vals >= 10 ? int ( $n_vals * 0.95 ) : $n_vals; + $statistical_metrics->{cutoff} = $cutoff; + + my $total_left = $n_vals; + my $top_vals = $n_vals - $cutoff; # vals > 95th + my $sum_excl = 0; + my $sum = 0; + my $sumsq = 0; + my $mid = int($n_vals / 2); + my $median = 0; + my $prev = NUM_BUCK-1; # Used for getting median when $cutoff is odd + my $bucket_95 = 0; # top bucket in 95th + + MKDEBUG && _d('total vals:', $total_left, 'top vals:', $top_vals, 'mid:', $mid); + + BUCKET: + for my $bucket ( reverse 0..(NUM_BUCK-1) ) { + my $val = $vals->[$bucket]; + next BUCKET unless $val; + + $total_left -= $val; + $sum_excl += $val; + $bucket_95 = $bucket if !$bucket_95 && $sum_excl > $top_vals; + + if ( !$median && $total_left <= $mid ) { + $median = (($cutoff % 2) || ($val > 1)) ? $buck_vals[$bucket] + : ($buck_vals[$bucket] + $buck_vals[$prev]) / 2; + } + + $sum += $val * $buck_vals[$bucket]; + $sumsq += $val * ($buck_vals[$bucket]**2); + $prev = $bucket; + } + + my $var = $sumsq/$n_vals - ( ($sum/$n_vals) ** 2 ); + my $stddev = $var > 0 ? sqrt($var) : 0; + my $maxstdev = (($args->{max} || 0) - ($args->{min} || 0)) / 2; + $stddev = $stddev > $maxstdev ? $maxstdev : $stddev; + + MKDEBUG && _d('sum:', $sum, 'sumsq:', $sumsq, 'stddev:', $stddev, + 'median:', $median, 'prev bucket:', $prev, + 'total left:', $total_left, 'sum excl', $sum_excl, + 'bucket 95:', $bucket_95, $buck_vals[$bucket_95]); + + $statistical_metrics->{stddev} = $stddev; + $statistical_metrics->{pct_95} = $buck_vals[$bucket_95]; + $statistical_metrics->{median} = $median; + + return $statistical_metrics; +} + +sub metrics { + my ( $self, %args ) = @_; + foreach my $arg ( qw(attrib where) ) { + die "I need a $arg argument" unless $args{$arg}; + } + my $stats = $self->results; + my $store = $stats->{classes}->{$args{where}}->{$args{attrib}}; + + my $global_cnt = $stats->{globals}->{$args{attrib}}->{cnt}; + my $metrics = $self->calculate_statistical_metrics($store->{all}, $store); + + return { + cnt => $store->{cnt}, + pct => $global_cnt && $store->{cnt} ? $store->{cnt} / $global_cnt : 0, + sum => $store->{sum}, + min => $store->{min}, + max => $store->{max}, + avg => $store->{sum} && $store->{cnt} ? $store->{sum} / $store->{cnt} : 0, + median => $metrics->{median}, + pct_95 => $metrics->{pct_95}, + stddev => $metrics->{stddev}, + }; +} + +sub top_events { + my ( $self, %args ) = @_; + my $classes = $self->{result_classes}; + my @sorted = reverse sort { # Sorted list of $groupby values + $classes->{$a}->{$args{attrib}}->{$args{orderby}} + <=> $classes->{$b}->{$args{attrib}}->{$args{orderby}} + } grep { + defined $classes->{$_}->{$args{attrib}}->{$args{orderby}} + } keys %$classes; + my @chosen; + my ($total, $count) = (0, 0); + foreach my $groupby ( @sorted ) { + if ( + (!$args{total} || $total < $args{total} ) + && ( !$args{count} || $count < $args{count} ) + ) { + push @chosen, [$groupby, 'top']; + } + + elsif ( $args{ol_attrib} && (!$args{ol_freq} + || $classes->{$groupby}->{$args{ol_attrib}}->{cnt} >= $args{ol_freq}) + ) { + MKDEBUG && _d('Calculating statistical_metrics'); + my $stats = $self->calculate_statistical_metrics( + $classes->{$groupby}->{$args{ol_attrib}}->{all}, + $classes->{$groupby}->{$args{ol_attrib}} + ); + if ( $stats->{pct_95} >= $args{ol_limit} ) { + push @chosen, [$groupby, 'outlier']; + } + } + + $total += $classes->{$groupby}->{$args{attrib}}->{$args{orderby}}; + $count++; + } + return @chosen; +} + +sub add_new_attributes { + my ( $self, $event ) = @_; + return unless $event; + + map { + my $attrib = $_; + $self->{attributes}->{$attrib} = [$attrib]; + $self->{alt_attribs}->{$attrib} = make_alt_attrib($attrib); + push @{$self->{all_attribs}}, $attrib; + MKDEBUG && _d('Added new attribute:', $attrib); + } + grep { + $_ ne $self->{groupby} + && !exists $self->{attributes}->{$_} + && !exists $self->{ignore_attribs}->{$_} + } + keys %$event; + + return; +} + +sub get_attributes { + my ( $self ) = @_; + return @{$self->{all_attribs}}; +} + +sub events_processed { + my ( $self ) = @_; + return $self->{n_events}; +} + +sub make_alt_attrib { + my ( @attribs ) = @_; + + my $attrib = shift @attribs; # Primary attribute. + return sub {} unless @attribs; # No alternates. + + my @lines; + push @lines, 'sub { my ( $event ) = @_; my $alt_attrib;'; + push @lines, map { + "\$alt_attrib = '$_' if !defined \$alt_attrib " + . "&& exists \$event->{'$_'};" + } @attribs; + push @lines, 'return $alt_attrib; }'; + MKDEBUG && _d('alt attrib sub for', $attrib, ':', @lines); + my $sub = eval join("\n", @lines); + die if $EVAL_ERROR; + return $sub; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End EventAggregator package +# ########################################################################### + +# ########################################################################### +# ReportFormatter package 5113 +# ########################################################################### +package ReportFormatter; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); +use List::Util qw(min max); + +use constant MKDEBUG => $ENV{MKDEBUG}; + +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; +$Data::Dumper::Quotekeys = 0; + +sub new { + my ( $class, %args ) = @_; + my @required_args = qw(); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my $self = { + underline_header => 1, + line_prefix => '# ', + line_width => 78, + truncate_underline => 1, + %args, + }; + return bless $self, $class; +} + +sub set_title { + my ( $self, $title ) = @_; + $self->{title} = $title; + return; +} + +sub set_columns { + my ( $self, @cols ) = @_; + push @{$self->{cols}}, map { + my $col = $_; + die "Column does not have a name" unless defined $col->{name}; + if ( $col->{fixed_wdith} && $col->{fixed_width} < length $col->{name} ) { + die "Fixed width is less than the column name"; + } + $col->{min_val_width} = length $col->{name}; + $col->{max_val_width} = length $col->{name}; + $col; + } @cols; + return; +} + +sub add_line { + my ( $self, @vals ) = @_; + + my $n_cols = scalar @{$self->{cols}}; + my $n_vals = scalar @vals; + die "Number of columns ($n_cols) and values ($n_vals) do not match" + unless $n_cols == $n_vals; + + my @line; + for my $i ( 0..$#vals ) { + my $col = $self->{cols}->[$i]; + my $val = $vals[$i]; + my $width = length $val; + if ( $col->{fixed_width} && $width > $col->{fixed_width} ) { + if ( $col->{truncate} ) { + $val = substr($val, 0, $col->{fixed_width} - 3); + $val .= '...'; + MKDEBUG && _d('Truncated', $vals[$i], 'to', $val); + } + else { + die "Value '$val' is too wide for column $col->{name}"; + } + } + $col->{max_val_width} = max($width, $col->{max_val_width}); + push @line, $val; + } + push @{$self->{lines}}, \@line; + + return; +} + +sub get_report { + my ( $self ) = @_; + my @lines; + my $p = $self->{line_prefix} || ''; + + my $n_cols = scalar @{$self->{cols}} + - ($self->{long_last_column} ? 2 : 1); + + my $fmt = $p; + my @col_fmts; + for my $i ( 0..$n_cols ) { + my $col = $self->{cols}->[$i]; + my $col_fmt = '%' + . ($col->{right_justify} ? '' : '-') + . "$col->{max_val_width}" + . 's'; + push @col_fmts, $col_fmt; + } + if ( $self->{long_last_column} ) { + push @col_fmts, '%s'; + } + $fmt .= join(' ', @col_fmts); + MKDEBUG && _d('Format:', $fmt); + + push @lines, sprintf "${p}$self->{title}" if $self->{title}; + + (my $hdr_fmt = $fmt) =~ s/%([^-])/%-$1/g; + push @lines, sprintf $hdr_fmt, map { $_->{name} } @{$self->{cols}}; + + if ( $self->{underline_header} ) { + my $underline_len = 0; + my @underlines = map { + my $underline = '=' x $_->{max_val_width}; + $underline_len += length $underline; + $underline; + } @{$self->{cols}}; + $underline_len += (scalar @underlines) - 1; + if ( $self->{truncate_underline} + && (2 + $underline_len) > $self->{line_width} ) { + my $over = $self->{line_width} - (2 + $underline_len); + $underlines[-1] = substr($underlines[-1], 0, $over); + } + + push @lines, sprintf $fmt, @underlines; + } + + foreach my $line ( @{$self->{lines}} ) { + push @lines, sprintf $fmt, @$line; + } + + return join("\n", @lines) . "\n"; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End ReportFormatter package +# ########################################################################### + +# ########################################################################### +# QueryReportFormatter package 5180 +# ########################################################################### + + +package QueryReportFormatter; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); +Transformers->import( + qw(shorten micro_t parse_timestamp unix_timestamp + make_checksum percentage_of)); + +use constant MKDEBUG => $ENV{MKDEBUG}; +use constant LINE_LENGTH => 74; +use constant MAX_STRING_LENGTH => 10; + +my %formatting_function = ( + ts => sub { + my ( $stats ) = @_; + my $min = parse_timestamp($stats->{min} || ''); + my $max = parse_timestamp($stats->{max} || ''); + return $min && $max ? "$min to $max" : ''; + }, +); + +my $bool_format = '# %3s%% %-6s %s'; + +sub new { + my ( $class, %args ) = @_; + return bless { }, $class; +} + +sub header { + my ($self) = @_; + + my ( $rss, $vsz, $user, $system ) = ( 0, 0, 0, 0 ); + my $result = ''; + eval { + my $mem = `ps -o rss,vsz -p $PID 2>&1`; + ( $rss, $vsz ) = $mem =~ m/(\d+)/g; + ( $user, $system ) = times(); + $result = sprintf "# %s user time, %s system time, %s rss, %s vsz\n", + micro_t( $user, p_s => 1, p_ms => 1 ), + micro_t( $system, p_s => 1, p_ms => 1 ), + shorten( ($rss || 0) * 1_024 ), + shorten( ($vsz || 0) * 1_024 ); + }; + if ( $EVAL_ERROR ) { + MKDEBUG && _d($EVAL_ERROR); + } + return $result; +} + +sub global_report { + my ( $self, $ea, %opts ) = @_; + my $stats = $ea->results; + my @result; + + my $global_cnt = $stats->{globals}->{$opts{worst}}->{cnt} || 0; + + my ($qps, $conc) = (0, 0); + if ( $global_cnt && $stats->{globals}->{ts} + && ($stats->{globals}->{ts}->{max} || '') + gt ($stats->{globals}->{ts}->{min} || '') + ) { + eval { + my $min = parse_timestamp($stats->{globals}->{ts}->{min}); + my $max = parse_timestamp($stats->{globals}->{ts}->{max}); + my $diff = unix_timestamp($max) - unix_timestamp($min); + $qps = $global_cnt / $diff; + $conc = $stats->{globals}->{$opts{worst}}->{sum} / $diff; + }; + } + + MKDEBUG && _d('global_cnt:', $global_cnt, 'unique:', + scalar keys %{$stats->{classes}}, 'qps:', $qps, 'conc:', $conc); + my $line = sprintf( + '# Overall: %s total, %s unique, %s QPS, %sx concurrency ', + shorten($global_cnt, d=>1_000), + shorten(scalar keys %{$stats->{classes}}, d=>1_000), + shorten($qps || 0, d=>1_000), + shorten($conc || 0, d=>1_000)); + $line .= ('_' x (LINE_LENGTH - length($line))); + push @result, $line; + + my ($format, @headers) = make_header('global'); + push @result, sprintf($format, '', @headers); + + foreach my $attrib ( sort_attribs($ea, @{$opts{select}}) ) { + my $attrib_type = $ea->type_for($attrib); + next unless $attrib_type; + next unless exists $stats->{globals}->{$attrib}; + if ( $formatting_function{$attrib} ) { # Handle special cases + push @result, sprintf $format, make_label($attrib), + $formatting_function{$attrib}->($stats->{globals}->{$attrib}), + (map { '' } 0..9); # just for good measure + } + else { + my $store = $stats->{globals}->{$attrib}; + my @values; + if ( $attrib_type eq 'num' ) { + my $func = $attrib =~ m/time$/ ? \µ_t : \&shorten; + MKDEBUG && _d('Calculating global statistical_metrics for', $attrib); + my $metrics = $ea->calculate_statistical_metrics($store->{all}, $store); + @values = ( + @{$store}{qw(sum min max)}, + $store->{sum} / $store->{cnt}, + @{$metrics}{qw(pct_95 stddev median)}, + ); + @values = map { defined $_ ? $func->($_) : '' } @values; + } + elsif ( $attrib_type eq 'string' ) { + MKDEBUG && _d('Ignoring string attrib', $attrib); + next; + } + elsif ( $attrib_type eq 'bool' ) { + if ( $store->{sum} > 0 || !$opts{no_zero_bool} ) { + push @result, + sprintf $bool_format, format_bool_attrib($store), $attrib; + } + } + else { + @values = ('', $store->{min}, $store->{max}, '', '', '', ''); + } + + push @result, sprintf $format, make_label($attrib), @values + unless $attrib_type eq 'bool'; # bool does its own thing. + } + } + + return join("\n", map { s/\s+$//; $_ } @result) . "\n"; +} + +sub event_report { + my ( $self, $ea, %opts ) = @_; + my $stats = $ea->results; + my @result; + + my $store = $stats->{classes}->{$opts{where}}; + return "# No such event $opts{where}\n" unless $store; + my $sample = $stats->{samples}->{$opts{where}}; + + my $global_cnt = $stats->{globals}->{$opts{worst}}->{cnt}; + my $class_cnt = $store->{$opts{worst}}->{cnt}; + + my ($qps, $conc) = (0, 0); + if ( $global_cnt && $store->{ts} + && ($store->{ts}->{max} || '') + gt ($store->{ts}->{min} || '') + ) { + eval { + my $min = parse_timestamp($store->{ts}->{min}); + my $max = parse_timestamp($store->{ts}->{max}); + my $diff = unix_timestamp($max) - unix_timestamp($min); + $qps = $class_cnt / $diff; + $conc = $store->{$opts{worst}}->{sum} / $diff; + }; + } + + my $line = sprintf( + '# %s %d: %s QPS, %sx concurrency, ID 0x%s at byte %d ', + ($ea->{groupby} eq 'fingerprint' ? 'Query' : 'Item'), + $opts{rank} || 0, + shorten($qps || 0, d=>1_000), + shorten($conc || 0, d=>1_000), + make_checksum($opts{where}), + $sample->{pos_in_log} || 0); + $line .= ('_' x (LINE_LENGTH - length($line))); + push @result, $line; + + if ( $opts{reason} ) { + push @result, "# This item is included in the report because it matches " + . ($opts{reason} eq 'top' ? '--limit.' : '--outliers.'); + } + + my ($format, @headers) = make_header(); + push @result, sprintf($format, '', @headers); + + push @result, sprintf + $format, 'Count', percentage_of($class_cnt, $global_cnt), $class_cnt, + map { '' } (1 ..9); + + foreach my $attrib ( sort_attribs($ea, @{$opts{select}}) ) { + my $attrib_type = $ea->type_for($attrib); + next unless $attrib_type; + next unless exists $store->{$attrib}; + my $vals = $store->{$attrib}; + next unless scalar %$vals; + if ( $formatting_function{$attrib} ) { # Handle special cases + push @result, sprintf $format, make_label($attrib), + $formatting_function{$attrib}->($vals), + (map { '' } 0..9); # just for good measure + } + else { + my @values; + my $pct; + if ( $attrib_type eq 'num' ) { + my $func = $attrib =~ m/time$/ ? \µ_t : \&shorten; + my $metrics = $ea->calculate_statistical_metrics($vals->{all}, $vals); + @values = ( + @{$vals}{qw(sum min max)}, + $vals->{sum} / $vals->{cnt}, + @{$metrics}{qw(pct_95 stddev median)}, + ); + @values = map { defined $_ ? $func->($_) : '' } @values; + $pct = percentage_of($vals->{sum}, + $stats->{globals}->{$attrib}->{sum}); + } + elsif ( $attrib_type eq 'string' ) { + push @values, + format_string_list($vals), + (map { '' } 0..9); # just for good measure + $pct = ''; + } + elsif ( $attrib_type eq 'bool' ) { + if ( $vals->{sum} > 0 || !$opts{no_zero_bool} ) { + push @result, + sprintf $bool_format, format_bool_attrib($vals), $attrib; + } + } + else { + @values = ('', $vals->{min}, $vals->{max}, '', '', '', ''); + $pct = 0; + } + + push @result, sprintf $format, make_label($attrib), $pct, @values + unless $attrib_type eq 'bool'; # bool does its own thing. + } + } + + return join("\n", map { s/\s+$//; $_ } @result) . "\n"; +} + +sub chart_distro { + my ( $self, $ea, %opts ) = @_; + my $stats = $ea->results; + my $store = $stats->{classes}->{$opts{where}}->{$opts{attribute}}; + my $vals = $store->{all}; + return "" unless defined $vals && scalar @$vals; + my @buck_tens = $ea->buckets_of(10); + my @distro = map { 0 } (0 .. 7); + map { $distro[$buck_tens[$_]] += $vals->[$_] } (1 .. @$vals - 1); + + my $max_val = 0; + my $vals_per_mark; # number of vals represented by 1 #-mark + my $max_disp_width = 64; + my $bar_fmt = "# %5s%s"; + my @distro_labels = qw(1us 10us 100us 1ms 10ms 100ms 1s 10s+); + my @results = "# $opts{attribute} distribution"; + + foreach my $n_vals ( @distro ) { + $max_val = $n_vals if $n_vals > $max_val; + } + $vals_per_mark = $max_val / $max_disp_width; + + foreach my $i ( 0 .. $#distro ) { + my $n_vals = $distro[$i]; + my $n_marks = $n_vals / ($vals_per_mark || 1); + $n_marks = 1 if $n_marks < 1 && $n_vals > 0; + my $bar = ($n_marks ? ' ' : '') . '#' x $n_marks; + push @results, sprintf $bar_fmt, $distro_labels[$i], $bar; + } + + return join("\n", @results) . "\n"; +} + +sub make_header { + my ( $global ) = @_; + my $format = "# %-9s %6s %7s %7s %7s %7s %7s %7s %7s"; + my @headers = qw(pct total min max avg 95% stddev median); + if ( $global ) { + $format =~ s/%(\d+)s/' ' x $1/e; + shift @headers; + } + return $format, @headers; +} + +sub make_label { + my ( $val ) = @_; + + if ( $val =~ m/^InnoDB/ ) { + $val =~ s/^InnoDB_(\w+)/IDB_$1/; + $val =~ s/r_(\w+)/r$1/; + } + + return $val eq 'ts' ? 'Time range' + : $val eq 'user' ? 'Users' + : $val eq 'db' ? 'Databases' + : $val eq 'Query_time' ? 'Exec time' + : $val eq 'host' ? 'Hosts' + : $val eq 'Error_no' ? 'Errors' + : do { $val =~ s/_/ /g; $val = substr($val, 0, 9); $val }; +} + +sub format_bool_attrib { + my ( $stats ) = @_; + my $p_true = percentage_of($stats->{sum}, $stats->{cnt}); + my $n_true = '(' . shorten($stats->{sum} || 0, d=>1_000, p=>0) . ')'; + return $p_true, $n_true; +} + +sub format_string_list { + my ( $stats ) = @_; + if ( exists $stats->{unq} ) { + my $cnt_for = $stats->{unq}; + if ( 1 == keys %$cnt_for ) { + my ($str) = keys %$cnt_for; + $str = substr($str, 0, LINE_LENGTH - 30) . '...' + if length $str > LINE_LENGTH - 30; + return (1, $str); + } + my $line = ''; + my @top = sort { $cnt_for->{$b} <=> $cnt_for->{$a} || $a cmp $b } + keys %$cnt_for; + my $i = 0; + foreach my $str ( @top ) { + my $print_str; + if ( length $str > MAX_STRING_LENGTH ) { + $print_str = substr($str, 0, MAX_STRING_LENGTH) . '...'; + } + else { + $print_str = $str; + } + last if (length $line) + (length $print_str) > LINE_LENGTH - 27; + $line .= "$print_str ($cnt_for->{$str}), "; + $i++; + } + $line =~ s/, $//; + if ( $i < @top ) { + $line .= "... " . (@top - $i) . " more"; + } + return (scalar keys %$cnt_for, $line); + } + else { + return ($stats->{cnt}); + } +} + +sub sort_attribs { + my ( $ea, @attribs ) = @_; + my %basic_attrib = ( + Query_time => 0, + Lock_time => 1, + Rows_sent => 2, + Rows_examined => 3, + user => 4, + host => 5, + db => 6, + ts => 7, + ); + my @basic_attribs; + my @non_bool_attribs; + my @bool_attribs; + + ATTRIB: + foreach my $attrib ( @attribs ) { + if ( exists $basic_attrib{$attrib} ) { + push @basic_attribs, $attrib; + } + else { + if ( ($ea->type_for($attrib) || '') ne 'bool' ) { + push @non_bool_attribs, $attrib; + } + else { + push @bool_attribs, $attrib; + } + } + } + + @non_bool_attribs = sort { uc $a cmp uc $b } @non_bool_attribs; + @bool_attribs = sort { uc $a cmp uc $b } @bool_attribs; + @basic_attribs = sort { + $basic_attrib{$a} <=> $basic_attrib{$b} } @basic_attribs; + + return @basic_attribs, @non_bool_attribs, @bool_attribs; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End QueryReportFormatter package +# ########################################################################### + +# ########################################################################### +# EventTimeline package 3539 +# ########################################################################### + + +package EventTimeline; + + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); +Transformers->import(qw(parse_timestamp secs_to_time unix_timestamp)); + +use constant MKDEBUG => $ENV{MKDEBUG}; +use constant KEY => 0; +use constant CNT => 1; +use constant ATT => 2; + +sub new { + my ( $class, %args ) = @_; + foreach my $arg ( qw(groupby attributes) ) { + die "I need a $arg argument" unless $args{$arg}; + } + + my %is_groupby = map { $_ => 1 } @{$args{groupby}}; + + return bless { + groupby => $args{groupby}, + attributes => [ grep { !$is_groupby{$_} } @{$args{attributes}} ], + results => [], + }, $class; +} + +sub reset_aggregated_data { + my ( $self ) = @_; + $self->{results} = []; +} + +sub aggregate { + my ( $self, $event ) = @_; + my $handler = $self->{handler}; + if ( !$handler ) { + $handler = $self->make_handler($event); + $self->{handler} = $handler; + } + return unless $handler; + $handler->($event); +} + +sub results { + my ( $self ) = @_; + return $self->{results}; +} + +sub make_handler { + my ( $self, $event ) = @_; + + my $float_re = qr{[+-]?(?:(?=\d|[.])\d*(?:[.])\d{0,})?(?:[E](?:[+-]?\d+)|)}i; + my @lines; # lines of code for the subroutine + + foreach my $attrib ( @{$self->{attributes}} ) { + my ($val) = $event->{$attrib}; + next unless defined $val; # Can't decide type if it's undef. + + my $type = $val =~ m/^(?:\d+|$float_re)$/o ? 'num' + : $val =~ m/^(?:Yes|No)$/ ? 'bool' + : 'string'; + MKDEBUG && _d('Type for', $attrib, 'is', $type, '(sample:', $val, ')'); + $self->{type_for}->{$attrib} = $type; + + push @lines, ( + "\$val = \$event->{$attrib};", + 'defined $val && do {', + "# type: $type", + "\$store = \$last->[ATT]->{$attrib} ||= {};", + ); + + if ( $type eq 'bool' ) { + push @lines, q{$val = $val eq 'Yes' ? 1 : 0;}; + $type = 'num'; + } + my $op = $type eq 'num' ? '<' : 'lt'; + push @lines, ( + '$store->{min} = $val if !defined $store->{min} || $val ' + . $op . ' $store->{min};', + ); + $op = ($type eq 'num') ? '>' : 'gt'; + push @lines, ( + '$store->{max} = $val if !defined $store->{max} || $val ' + . $op . ' $store->{max};', + ); + if ( $type eq 'num' ) { + push @lines, '$store->{sum} += $val;'; + } + push @lines, '};'; + } + + unshift @lines, ( + 'sub {', + 'my ( $event ) = @_;', + 'my ($val, $last, $store);', # NOTE: define all variables here + '$last = $results->[-1];', + 'if ( !$last || ' + . join(' || ', + map { "\$last->[KEY]->[$_] ne (\$event->{$self->{groupby}->[$_]} || 0)" } + (0 .. @{$self->{groupby}} -1)) + . ' ) {', + ' $last = [[' + . join(', ', + map { "(\$event->{$self->{groupby}->[$_]} || 0)" } + (0 .. @{$self->{groupby}} -1)) + . '], 0, {} ];', + ' push @$results, $last;', + '}', + '++$last->[CNT];', + ); + push @lines, '}'; + my $results = $self->{results}; # Referred to by the eval + my $code = join("\n", @lines); + $self->{code} = $code; + + MKDEBUG && _d('Timeline handler:', $code); + my $sub = eval $code; + die if $EVAL_ERROR; + return $sub; +} + +sub report { + my ( $self, $results, $callback ) = @_; + $callback->("# " . ('#' x 72) . "\n"); + $callback->("# " . join(',', @{$self->{groupby}}) . " report\n"); + $callback->("# " . ('#' x 72) . "\n"); + foreach my $res ( @$results ) { + my $t; + my @vals; + if ( ($t = $res->[ATT]->{ts}) && $t->{min} ) { + my $min = parse_timestamp($t->{min}); + push @vals, $min; + if ( $t->{max} && $t->{max} gt $t->{min} ) { + my $max = parse_timestamp($t->{max}); + my $diff = secs_to_time(unix_timestamp($max) - unix_timestamp($min)); + push @vals, $diff; + } + else { + push @vals, '0:00'; + } + } + else { + push @vals, ('', ''); + } + $callback->(sprintf("# %19s %7s %3d %s\n", @vals, $res->[CNT], $res->[KEY]->[0])); + } +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End EventTimeline package +# ########################################################################### + +# ########################################################################### +# QueryParser package 4977 +# ########################################################################### +package QueryParser; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); + +use constant MKDEBUG => $ENV{MKDEBUG}; +our $tbl_ident = qr/(?:`[^`]+`|\w+)(?:\.(?:`[^`]+`|\w+))?/; +our $tbl_regex = qr{ + \b(?:FROM|JOIN|(?get_tables($select); + } + my ($tbl) = $query =~ m/TABLE\s+($tbl_ident)(\s+.*)?/i; + MKDEBUG && _d('Matches table:', $tbl); + return ($tbl); + } + + $query =~ s/ (?:LOW_PRIORITY|IGNORE|STRAIGHT_JOIN)//ig; + + if ( $query =~ /^\s*LOCK TABLES/i ) { + MKDEBUG && _d('Special table type: LOCK TABLES'); + $query =~ s/^(\s*LOCK TABLES\s+)//; + $query =~ s/\s+(?:READ|WRITE|LOCAL)+\s*//g; + MKDEBUG && _d('Locked tables:', $query); + $query = "FROM $query"; + } + + $query =~ s/\\["']//g; # quoted strings + $query =~ s/".*?"/?/sg; # quoted strings + $query =~ s/'.*?'/?/sg; # quoted strings + + my @tables; + foreach my $tbls ( $query =~ m/$tbl_regex/gio ) { + MKDEBUG && _d('Match tables:', $tbls); + foreach my $tbl ( split(',', $tbls) ) { + $tbl =~ s/\s*($tbl_ident)(\s+.*)?/$1/gio; + + if ( $tbl !~ m/[a-zA-Z]/ ) { + MKDEBUG && _d('Skipping suspicious table name:', $tbl); + next; + } + + push @tables, $tbl; + } + } + return @tables; +} + +sub has_derived_table { + my ( $self, $query ) = @_; + my $match = $query =~ m/$has_derived/; + MKDEBUG && _d($query, 'has ' . ($match ? 'a' : 'no') . ' derived table'); + return $match; +} + +sub get_aliases { + my ( $self, $query ) = @_; + return unless $query; + my $aliases; + + $query =~ s/ (?:LOW_PRIORITY|IGNORE|STRAIGHT_JOIN)//ig; + + $query =~ s/ (?:INNER|OUTER|CROSS|LEFT|RIGHT|NATURAL)//ig; + + my ($tbl_refs, $from) = $query =~ m{ + ( + (FROM|INTO|UPDATE)\b\s* # Keyword before table refs + .+? # Table refs + ) + (?:\s+|\z) # If the query does not end with the table + (?:WHERE|ORDER|LIMIT|HAVING|SET|VALUES|\z) # Keyword after table refs + }ix; + + die "Failed to parse table references from $query" + unless $tbl_refs && $from; + + MKDEBUG && _d('tbl refs:', $tbl_refs); + + my $before_tbl = qr/(?:,|JOIN|\s|$from)+/i; + + my $after_tbl = qr/(?:,|JOIN|ON|USING|\z)/i; + + $tbl_refs =~ s/ = /=/g; + + while ( + $tbl_refs =~ m{ + $before_tbl\b\s* + ( ($tbl_ident) (?:\s+ (?:AS\s+)? (\w+))? ) + \s*$after_tbl + }xgio ) + { + my ( $tbl_ref, $db_tbl, $alias ) = ($1, $2, $3); + MKDEBUG && _d('Match table:', $tbl_ref); + + if ( $tbl_ref =~ m/^AS\s+\w+/i ) { + MKDEBUG && _d('Subquery', $tbl_ref); + $aliases->{$alias} = undef; + next; + } + + my ( $db, $tbl ) = $db_tbl =~ m/^(?:(.*?)\.)?(.*)/; + $aliases->{$alias || $tbl} = $tbl; + $aliases->{DATABASE}->{$tbl} = $db if $db; + } + return $aliases; +} + +sub split { + my ( $self, $query ) = @_; + return unless $query; + $query = $self->clean_query($query); + MKDEBUG && _d('Splitting', $query); + + my $verbs = qr{SELECT|INSERT|UPDATE|DELETE|REPLACE|UNION|CREATE}i; + + my @split_statements = grep { $_ } split(m/\b($verbs\b(?!(?:\s*\()))/io, $query); + + my @statements; + if ( @split_statements == 1 ) { + push @statements, $query; + } + else { + for ( my $i = 0; $i <= $#split_statements; $i += 2 ) { + push @statements, $split_statements[$i].$split_statements[$i+1]; + + if ( $statements[-2] && $statements[-2] =~ m/on duplicate key\s+$/i ) { + $statements[-2] .= pop @statements; + } + } + } + + MKDEBUG && _d('statements:', map { $_ ? "<$_>" : 'none' } @statements); + return @statements; +} + +sub clean_query { + my ( $self, $query ) = @_; + return unless $query; + $query =~ s!/\*.*?\*/! !g; # Remove /* comment blocks */ + $query =~ s/^\s+//; # Remove leading spaces + $query =~ s/\s+$//; # Remove trailing spaces + $query =~ s/\s{2,}/ /g; # Remove extra spaces + return $query; +} + +sub split_subquery { + my ( $self, $query ) = @_; + return unless $query; + $query = $self->clean_query($query); + $query =~ s/;$//; + + my @subqueries; + my $sqno = 0; # subquery number + my $pos = 0; + while ( $query =~ m/(\S+)(?:\s+|\Z)/g ) { + $pos = pos($query); + my $word = $1; + MKDEBUG && _d($word, $sqno); + if ( $word =~ m/^\(?SELECT\b/i ) { + my $start_pos = $pos - length($word) - 1; + if ( $start_pos ) { + $sqno++; + MKDEBUG && _d('Subquery', $sqno, 'starts at', $start_pos); + $subqueries[$sqno] = { + start_pos => $start_pos, + end_pos => 0, + len => 0, + words => [$word], + lp => 1, # left parentheses + rp => 0, # right parentheses + done => 0, + }; + } + else { + MKDEBUG && _d('Main SELECT at pos 0'); + } + } + else { + next unless $sqno; # next unless we're in a subquery + MKDEBUG && _d('In subquery', $sqno); + my $sq = $subqueries[$sqno]; + if ( $sq->{done} ) { + MKDEBUG && _d('This subquery is done; SQL is for', + ($sqno - 1 ? "subquery $sqno" : "the main SELECT")); + next; + } + push @{$sq->{words}}, $word; + my $lp = ($word =~ tr/\(//) || 0; + my $rp = ($word =~ tr/\)//) || 0; + MKDEBUG && _d('parentheses left', $lp, 'right', $rp); + if ( ($sq->{lp} + $lp) - ($sq->{rp} + $rp) == 0 ) { + my $end_pos = $pos - 1; + MKDEBUG && _d('Subquery', $sqno, 'ends at', $end_pos); + $sq->{end_pos} = $end_pos; + $sq->{len} = $end_pos - $sq->{start_pos}; + } + } + } + + for my $i ( 1..$#subqueries ) { + my $sq = $subqueries[$i]; + next unless $sq; + $sq->{sql} = join(' ', @{$sq->{words}}); + substr $query, + $sq->{start_pos} + 1, # +1 for ( + $sq->{len} - 1, # -1 for ) + "__subquery_$i"; + } + + return $query, map { $_->{sql} } grep { defined $_ } @subqueries; +} + +sub query_type { + my ( $self, $query, $qr ) = @_; + my ($type, undef) = $qr->_distill_verbs($query); + my $rw; + if ( $type =~ m/^SELECT\b/ ) { + $rw = 'read'; + } + elsif ( $type =~ m/^$data_manip_stmts\b/ + || $type =~ m/^$data_def_stmts\b/ ) { + $rw = 'write' + } + + return { + type => $type, + rw => $rw, + } +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End QueryParser package +# ########################################################################### + +# ########################################################################### +# MySQLDump package 4160 +# ########################################################################### +package MySQLDump; + +use strict; +use warnings FATAL => 'all'; + +use English qw(-no_match_vars); + +use constant MKDEBUG => $ENV{MKDEBUG}; + +( our $before = <<'EOF') =~ s/^ //gm; + /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; + /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; + /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; + /*!40101 SET NAMES utf8 */; + /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; + /*!40103 SET TIME_ZONE='+00:00' */; + /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; + /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; + /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; + /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; +EOF + +( our $after = <<'EOF') =~ s/^ //gm; + /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; + /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; + /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; + /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; + /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; + /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; + /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; +EOF + +sub new { + my ( $class, %args ) = @_; + $args{cache} = 1 unless defined $args{cache}; + my $self = bless \%args, $class; + return $self; +} + +sub dump { + my ( $self, $dbh, $quoter, $db, $tbl, $what ) = @_; + + if ( $what eq 'table' ) { + my $ddl = $self->get_create_table($dbh, $quoter, $db, $tbl); + return unless $ddl; + if ( $ddl->[0] eq 'table' ) { + return $before + . 'DROP TABLE IF EXISTS ' . $quoter->quote($tbl) . ";\n" + . $ddl->[1] . ";\n"; + } + else { + return 'DROP TABLE IF EXISTS ' . $quoter->quote($tbl) . ";\n" + . '/*!50001 DROP VIEW IF EXISTS ' + . $quoter->quote($tbl) . "*/;\n/*!50001 " + . $self->get_tmp_table($dbh, $quoter, $db, $tbl) . "*/;\n"; + } + } + elsif ( $what eq 'triggers' ) { + my $trgs = $self->get_triggers($dbh, $quoter, $db, $tbl); + if ( $trgs && @$trgs ) { + my $result = $before . "\nDELIMITER ;;\n"; + foreach my $trg ( @$trgs ) { + if ( $trg->{sql_mode} ) { + $result .= qq{/*!50003 SET SESSION SQL_MODE='$trg->{sql_mode}' */;;\n}; + } + $result .= "/*!50003 CREATE */ "; + if ( $trg->{definer} ) { + my ( $user, $host ) + = map { s/'/''/g; "'$_'"; } + split('@', $trg->{definer}, 2); + $result .= "/*!50017 DEFINER=$user\@$host */ "; + } + $result .= sprintf("/*!50003 TRIGGER %s %s %s ON %s\nFOR EACH ROW %s */;;\n\n", + $quoter->quote($trg->{trigger}), + @{$trg}{qw(timing event)}, + $quoter->quote($trg->{table}), + $trg->{statement}); + } + $result .= "DELIMITER ;\n\n/*!50003 SET SESSION SQL_MODE=\@OLD_SQL_MODE */;\n\n"; + return $result; + } + else { + return undef; + } + } + elsif ( $what eq 'view' ) { + my $ddl = $self->get_create_table($dbh, $quoter, $db, $tbl); + return '/*!50001 DROP TABLE IF EXISTS ' . $quoter->quote($tbl) . "*/;\n" + . '/*!50001 DROP VIEW IF EXISTS ' . $quoter->quote($tbl) . "*/;\n" + . '/*!50001 ' . $ddl->[1] . "*/;\n"; + } + else { + die "You didn't say what to dump."; + } +} + +sub _use_db { + my ( $self, $dbh, $quoter, $new ) = @_; + if ( !$new ) { + MKDEBUG && _d('No new DB to use'); + return; + } + my $sql = 'SELECT DATABASE()'; + MKDEBUG && _d($sql); + my $curr = $dbh->selectrow_array($sql); + if ( $curr && $new && $curr eq $new ) { + MKDEBUG && _d('Current and new DB are the same'); + return $curr; + } + $sql = 'USE ' . $quoter->quote($new); + MKDEBUG && _d($sql); + $dbh->do($sql); + return $curr; +} + +sub get_create_table { + my ( $self, $dbh, $quoter, $db, $tbl ) = @_; + if ( !$self->{cache} || !$self->{tables}->{$db}->{$tbl} ) { + my $sql = '/*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, ' + . q{@@SQL_MODE := REPLACE(REPLACE(@@SQL_MODE, 'ANSI_QUOTES', ''), ',,', ','), } + . '@OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, ' + . '@@SQL_QUOTE_SHOW_CREATE := 1 */'; + MKDEBUG && _d($sql); + eval { $dbh->do($sql); }; + MKDEBUG && $EVAL_ERROR && _d($EVAL_ERROR); + my $curr_db = $self->_use_db($dbh, $quoter, $db); + $sql = "SHOW CREATE TABLE " . $quoter->quote($db, $tbl); + MKDEBUG && _d($sql); + my $href; + eval { $href = $dbh->selectrow_hashref($sql); }; + if ( $EVAL_ERROR ) { + warn "Failed to $sql. The table may be damaged.\nError: $EVAL_ERROR"; + return; + } + $self->_use_db($dbh, $quoter, $curr_db); + $sql = '/*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, ' + . '@@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */'; + MKDEBUG && _d($sql); + $dbh->do($sql); + my ($key) = grep { m/create table/i } keys %$href; + if ( $key ) { + MKDEBUG && _d('This table is a base table'); + $self->{tables}->{$db}->{$tbl} = [ 'table', $href->{$key} ]; + } + else { + MKDEBUG && _d('This table is a view'); + ($key) = grep { m/create view/i } keys %$href; + $self->{tables}->{$db}->{$tbl} = [ 'view', $href->{$key} ]; + } + } + return $self->{tables}->{$db}->{$tbl}; +} + +sub get_columns { + my ( $self, $dbh, $quoter, $db, $tbl ) = @_; + MKDEBUG && _d('Get columns for', $db, $tbl); + if ( !$self->{cache} || !$self->{columns}->{$db}->{$tbl} ) { + my $curr_db = $self->_use_db($dbh, $quoter, $db); + my $sql = "SHOW COLUMNS FROM " . $quoter->quote($db, $tbl); + MKDEBUG && _d($sql); + my $cols = $dbh->selectall_arrayref($sql, { Slice => {} }); + $self->_use_db($dbh, $quoter, $curr_db); + $self->{columns}->{$db}->{$tbl} = [ + map { + my %row; + @row{ map { lc $_ } keys %$_ } = values %$_; + \%row; + } @$cols + ]; + } + return $self->{columns}->{$db}->{$tbl}; +} + +sub get_tmp_table { + my ( $self, $dbh, $quoter, $db, $tbl ) = @_; + my $result = 'CREATE TABLE ' . $quoter->quote($tbl) . " (\n"; + $result .= join(",\n", + map { ' ' . $quoter->quote($_->{field}) . ' ' . $_->{type} } + @{$self->get_columns($dbh, $quoter, $db, $tbl)}); + $result .= "\n)"; + MKDEBUG && _d($result); + return $result; +} + +sub get_triggers { + my ( $self, $dbh, $quoter, $db, $tbl ) = @_; + if ( !$self->{cache} || !$self->{triggers}->{$db} ) { + $self->{triggers}->{$db} = {}; + my $sql = '/*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, ' + . q{@@SQL_MODE := REPLACE(REPLACE(@@SQL_MODE, 'ANSI_QUOTES', ''), ',,', ','), } + . '@OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, ' + . '@@SQL_QUOTE_SHOW_CREATE := 1 */'; + MKDEBUG && _d($sql); + eval { $dbh->do($sql); }; + MKDEBUG && $EVAL_ERROR && _d($EVAL_ERROR); + $sql = "SHOW TRIGGERS FROM " . $quoter->quote($db); + MKDEBUG && _d($sql); + my $sth = $dbh->prepare($sql); + $sth->execute(); + if ( $sth->rows ) { + my $trgs = $sth->fetchall_arrayref({}); + foreach my $trg (@$trgs) { + my %trg; + @trg{ map { lc $_ } keys %$trg } = values %$trg; + push @{ $self->{triggers}->{$db}->{ $trg{table} } }, \%trg; + } + } + $sql = '/*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, ' + . '@@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */'; + MKDEBUG && _d($sql); + $dbh->do($sql); + } + if ( $tbl ) { + return $self->{triggers}->{$db}->{$tbl}; + } + return values %{$self->{triggers}->{$db}}; +} + +sub get_databases { + my ( $self, $dbh, $quoter, $like ) = @_; + if ( !$self->{cache} || !$self->{databases} || $like ) { + my $sql = 'SHOW DATABASES'; + my @params; + if ( $like ) { + $sql .= ' LIKE ?'; + push @params, $like; + } + my $sth = $dbh->prepare($sql); + MKDEBUG && _d($sql, @params); + $sth->execute( @params ); + my @dbs = map { $_->[0] } @{$sth->fetchall_arrayref()}; + $self->{databases} = \@dbs unless $like; + return @dbs; + } + return @{$self->{databases}}; +} + +sub get_table_status { + my ( $self, $dbh, $quoter, $db, $like ) = @_; + if ( !$self->{cache} || !$self->{table_status}->{$db} || $like ) { + my $sql = "SHOW TABLE STATUS FROM " . $quoter->quote($db); + my @params; + if ( $like ) { + $sql .= ' LIKE ?'; + push @params, $like; + } + MKDEBUG && _d($sql, @params); + my $sth = $dbh->prepare($sql); + $sth->execute(@params); + my @tables = @{$sth->fetchall_arrayref({})}; + @tables = map { + my %tbl; # Make a copy with lowercased keys + @tbl{ map { lc $_ } keys %$_ } = values %$_; + $tbl{engine} ||= $tbl{type} || $tbl{comment}; + delete $tbl{type}; + \%tbl; + } @tables; + $self->{table_status}->{$db} = \@tables unless $like; + return @tables; + } + return @{$self->{table_status}->{$db}}; +} + +sub get_table_list { + my ( $self, $dbh, $quoter, $db, $like ) = @_; + if ( !$self->{cache} || !$self->{table_list}->{$db} || $like ) { + my $sql = "SHOW /*!50002 FULL*/ TABLES FROM " . $quoter->quote($db); + my @params; + if ( $like ) { + $sql .= ' LIKE ?'; + push @params, $like; + } + MKDEBUG && _d($sql, @params); + my $sth = $dbh->prepare($sql); + $sth->execute(@params); + my @tables = @{$sth->fetchall_arrayref()}; + @tables = map { + my %tbl = ( + name => $_->[0], + engine => ($_->[1] || '') eq 'VIEW' ? 'VIEW' : '', + ); + \%tbl; + } @tables; + $self->{table_list}->{$db} = \@tables unless $like; + return @tables; + } + return @{$self->{table_list}->{$db}}; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End MySQLDump package +# ########################################################################### + +# ########################################################################### +# TableParser package 5216 +# ########################################################################### +package TableParser; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; +$Data::Dumper::Quotekeys = 0; + +use constant MKDEBUG => $ENV{MKDEBUG}; + + +sub new { + my ( $class, %args ) = @_; + my @required_args = qw(Quoter); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my $self = { %args }; + return bless $self, $class; +} + + +sub parse { + my ( $self, $ddl, $opts ) = @_; + return unless $ddl; + if ( ref $ddl eq 'ARRAY' ) { + if ( lc $ddl->[0] eq 'table' ) { + $ddl = $ddl->[1]; + } + else { + return { + engine => 'VIEW', + }; + } + } + + if ( $ddl !~ m/CREATE (?:TEMPORARY )?TABLE `/ ) { + die "Cannot parse table definition; is ANSI quoting " + . "enabled or SQL_QUOTE_SHOW_CREATE disabled?"; + } + + my ($name) = $ddl =~ m/CREATE (?:TEMPORARY )?TABLE\s+(`.+?`)/; + (undef, $name) = $self->{Quoter}->split_unquote($name) if $name; + + $ddl =~ s/(`[^`]+`)/\L$1/g; + + my $engine = $self->get_engine($ddl); + + my @defs = $ddl =~ m/^(\s+`.*?),?$/gm; + my @cols = map { $_ =~ m/`([^`]+)`/ } @defs; + MKDEBUG && _d('Columns:', join(', ', @cols)); + + my %def_for; + @def_for{@cols} = @defs; + + my (@nums, @null); + my (%type_for, %is_nullable, %is_numeric, %is_autoinc); + foreach my $col ( @cols ) { + my $def = $def_for{$col}; + my ( $type ) = $def =~ m/`[^`]+`\s([a-z]+)/; + die "Can't determine column type for $def" unless $type; + $type_for{$col} = $type; + if ( $type =~ m/(?:(?:tiny|big|medium|small)?int|float|double|decimal|year)/ ) { + push @nums, $col; + $is_numeric{$col} = 1; + } + if ( $def !~ m/NOT NULL/ ) { + push @null, $col; + $is_nullable{$col} = 1; + } + $is_autoinc{$col} = $def =~ m/AUTO_INCREMENT/i ? 1 : 0; + } + + my ($keys, $clustered_key) = $self->get_keys($ddl, $opts, \%is_nullable); + + return { + name => $name, + cols => \@cols, + col_posn => { map { $cols[$_] => $_ } 0..$#cols }, + is_col => { map { $_ => 1 } @cols }, + null_cols => \@null, + is_nullable => \%is_nullable, + is_autoinc => \%is_autoinc, + clustered_key => $clustered_key, + keys => $keys, + defs => \%def_for, + numeric_cols => \@nums, + is_numeric => \%is_numeric, + engine => $engine, + type_for => \%type_for, + }; +} + +sub sort_indexes { + my ( $self, $tbl ) = @_; + + my @indexes + = sort { + (($a ne 'PRIMARY') <=> ($b ne 'PRIMARY')) + || ( !$tbl->{keys}->{$a}->{is_unique} <=> !$tbl->{keys}->{$b}->{is_unique} ) + || ( $tbl->{keys}->{$a}->{is_nullable} <=> $tbl->{keys}->{$b}->{is_nullable} ) + || ( scalar(@{$tbl->{keys}->{$a}->{cols}}) <=> scalar(@{$tbl->{keys}->{$b}->{cols}}) ) + } + grep { + $tbl->{keys}->{$_}->{type} eq 'BTREE' + } + sort keys %{$tbl->{keys}}; + + MKDEBUG && _d('Indexes sorted best-first:', join(', ', @indexes)); + return @indexes; +} + +sub find_best_index { + my ( $self, $tbl, $index ) = @_; + my $best; + if ( $index ) { + ($best) = grep { uc $_ eq uc $index } keys %{$tbl->{keys}}; + } + if ( !$best ) { + if ( $index ) { + die "Index '$index' does not exist in table"; + } + else { + ($best) = $self->sort_indexes($tbl); + } + } + MKDEBUG && _d('Best index found is', $best); + return $best; +} + +sub find_possible_keys { + my ( $self, $dbh, $database, $table, $quoter, $where ) = @_; + return () unless $where; + my $sql = 'EXPLAIN SELECT * FROM ' . $quoter->quote($database, $table) + . ' WHERE ' . $where; + MKDEBUG && _d($sql); + my $expl = $dbh->selectrow_hashref($sql); + $expl = { map { lc($_) => $expl->{$_} } keys %$expl }; + if ( $expl->{possible_keys} ) { + MKDEBUG && _d('possible_keys =', $expl->{possible_keys}); + my @candidates = split(',', $expl->{possible_keys}); + my %possible = map { $_ => 1 } @candidates; + if ( $expl->{key} ) { + MKDEBUG && _d('MySQL chose', $expl->{key}); + unshift @candidates, grep { $possible{$_} } split(',', $expl->{key}); + MKDEBUG && _d('Before deduping:', join(', ', @candidates)); + my %seen; + @candidates = grep { !$seen{$_}++ } @candidates; + } + MKDEBUG && _d('Final list:', join(', ', @candidates)); + return @candidates; + } + else { + MKDEBUG && _d('No keys in possible_keys'); + return (); + } +} + +sub check_table { + my ( $self, %args ) = @_; + my @required_args = qw(dbh db tbl); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my ($dbh, $db, $tbl) = @args{@required_args}; + my $q = $self->{Quoter}; + my $db_tbl = $q->quote($db, $tbl); + MKDEBUG && _d('Checking', $db_tbl); + + my $sql = "SHOW TABLES FROM " . $q->quote($db) + . ' LIKE ' . $q->literal_like($tbl); + MKDEBUG && _d($sql); + my $row; + eval { + $row = $dbh->selectrow_arrayref($sql); + }; + if ( $EVAL_ERROR ) { + MKDEBUG && _d($EVAL_ERROR); + return 0; + } + if ( !$row->[0] || $row->[0] ne $tbl ) { + MKDEBUG && _d('Table does not exist'); + return 0; + } + + MKDEBUG && _d('Table exists; no privs to check'); + return 1 unless $args{all_privs}; + + $sql = "SHOW FULL COLUMNS FROM $db_tbl"; + MKDEBUG && _d($sql); + eval { + $row = $dbh->selectrow_hashref($sql); + }; + if ( $EVAL_ERROR ) { + MKDEBUG && _d($EVAL_ERROR); + return 0; + } + if ( !scalar keys %$row ) { + MKDEBUG && _d('Table has no columns:', Dumper($row)); + return 0; + } + my $privs = $row->{privileges} || $row->{Privileges}; + + $sql = "DELETE FROM $db_tbl LIMIT 0"; + MKDEBUG && _d($sql); + eval { + $dbh->do($sql); + }; + my $can_delete = $EVAL_ERROR ? 0 : 1; + + MKDEBUG && _d('User privs on', $db_tbl, ':', $privs, + ($can_delete ? 'delete' : '')); + + if ( !($privs =~ m/select/ && $privs =~ m/insert/ && $privs =~ m/update/ + && $can_delete) ) { + MKDEBUG && _d('User does not have all privs'); + return 0; + } + + MKDEBUG && _d('User has all privs'); + return 1; +} + +sub get_engine { + my ( $self, $ddl, $opts ) = @_; + my ( $engine ) = $ddl =~ m/\).*?(?:ENGINE|TYPE)=(\w+)/; + MKDEBUG && _d('Storage engine:', $engine); + return $engine || undef; +} + +sub get_keys { + my ( $self, $ddl, $opts, $is_nullable ) = @_; + my $engine = $self->get_engine($ddl); + my $keys = {}; + my $clustered_key = undef; + + KEY: + foreach my $key ( $ddl =~ m/^ ((?:[A-Z]+ )?KEY .*)$/gm ) { + + next KEY if $key =~ m/FOREIGN/; + + my $key_ddl = $key; + MKDEBUG && _d('Parsed key:', $key_ddl); + + if ( $engine !~ m/MEMORY|HEAP/ ) { + $key =~ s/USING HASH/USING BTREE/; + } + + my ( $type, $cols ) = $key =~ m/(?:USING (\w+))? \((.+)\)/; + my ( $special ) = $key =~ m/(FULLTEXT|SPATIAL)/; + $type = $type || $special || 'BTREE'; + if ( $opts->{mysql_version} && $opts->{mysql_version} lt '004001000' + && $engine =~ m/HEAP|MEMORY/i ) + { + $type = 'HASH'; # MySQL pre-4.1 supports only HASH indexes on HEAP + } + + my ($name) = $key =~ m/(PRIMARY|`[^`]*`)/; + my $unique = $key =~ m/PRIMARY|UNIQUE/ ? 1 : 0; + my @cols; + my @col_prefixes; + foreach my $col_def ( split(',', $cols) ) { + my ($name, $prefix) = $col_def =~ m/`([^`]+)`(?:\((\d+)\))?/; + push @cols, $name; + push @col_prefixes, $prefix; + } + $name =~ s/`//g; + + MKDEBUG && _d('Key', $name, 'cols:', join(', ', @cols)); + + $keys->{$name} = { + name => $name, + type => $type, + colnames => $cols, + cols => \@cols, + col_prefixes => \@col_prefixes, + is_unique => $unique, + is_nullable => scalar(grep { $is_nullable->{$_} } @cols), + is_col => { map { $_ => 1 } @cols }, + ddl => $key_ddl, + }; + + if ( $engine =~ m/InnoDB/i && !$clustered_key ) { + my $this_key = $keys->{$name}; + if ( $this_key->{name} eq 'PRIMARY' ) { + $clustered_key = 'PRIMARY'; + } + elsif ( $this_key->{is_unique} && !$this_key->{is_nullable} ) { + $clustered_key = $this_key->{name}; + } + MKDEBUG && $clustered_key && _d('This key is the clustered key'); + } + } + + return $keys, $clustered_key; +} + +sub get_fks { + my ( $self, $ddl, $opts ) = @_; + my $fks = {}; + + foreach my $fk ( + $ddl =~ m/CONSTRAINT .* FOREIGN KEY .* REFERENCES [^\)]*\)/mg ) + { + my ( $name ) = $fk =~ m/CONSTRAINT `(.*?)`/; + my ( $cols ) = $fk =~ m/FOREIGN KEY \(([^\)]+)\)/; + my ( $parent, $parent_cols ) = $fk =~ m/REFERENCES (\S+) \(([^\)]+)\)/; + + if ( $parent !~ m/\./ && $opts->{database} ) { + $parent = "`$opts->{database}`.$parent"; + } + + $fks->{$name} = { + name => $name, + colnames => $cols, + cols => [ map { s/[ `]+//g; $_; } split(',', $cols) ], + parent_tbl => $parent, + parent_colnames=> $parent_cols, + parent_cols => [ map { s/[ `]+//g; $_; } split(',', $parent_cols) ], + ddl => $fk, + }; + } + + return $fks; +} + +sub remove_auto_increment { + my ( $self, $ddl ) = @_; + $ddl =~ s/(^\).*?) AUTO_INCREMENT=\d+\b/$1/m; + return $ddl; +} + +sub remove_secondary_indexes { + my ( $self, $ddl ) = @_; + my $sec_indexes_ddl; + my $tbl_struct = $self->parse($ddl); + + if ( ($tbl_struct->{engine} || '') =~ m/InnoDB/i ) { + my $clustered_key = $tbl_struct->{clustered_key}; + $clustered_key ||= ''; + + my @sec_indexes = map { + my $key_def = $_->{ddl}; + $key_def =~ s/([\(\)])/\\$1/g; + $ddl =~ s/\s+$key_def//; + "ADD $_->{ddl}"; + } + grep { $_->{name} ne $clustered_key } + values %{$tbl_struct->{keys}}; + MKDEBUG && _d('Secondary indexes:', Dumper(\@sec_indexes)); + + if ( @sec_indexes ) { + $sec_indexes_ddl = join(' ', @sec_indexes); + $sec_indexes_ddl =~ s/,$//; + } + + $ddl =~ s/,(\n\) )/$1/s; + } + else { + MKDEBUG && _d('Not removing secondary indexes from', + $tbl_struct->{engine}, 'table'); + } + + return $ddl, $sec_indexes_ddl, $tbl_struct; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End TableParser package +# ########################################################################### + +# ########################################################################### +# QueryReview package 3277 +# ########################################################################### + +package QueryReview; + + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); +Transformers->import(qw(make_checksum parse_timestamp)); + +use Data::Dumper; + +use constant MKDEBUG => $ENV{MKDEBUG}; + +my %basic_cols = map { $_ => 1 } + qw(checksum fingerprint sample first_seen last_seen reviewed_by + reviewed_on comments); +my %skip_cols = map { $_ => 1 } qw(fingerprint sample checksum); + +sub new { + my ( $class, %args ) = @_; + foreach my $arg ( qw(dbh db_tbl tbl_struct quoter) ) { + die "I need a $arg argument" unless $args{$arg}; + } + + foreach my $col ( keys %basic_cols ) { + die "Query review table $args{db_tbl} does not have a $col column" + unless $args{tbl_struct}->{is_col}->{$col}; + } + + my $now = defined $args{ts_default} ? $args{ts_default} : 'NOW()'; + + my $sql = <<" SQL"; + INSERT INTO $args{db_tbl} + (checksum, fingerprint, sample, first_seen, last_seen) + VALUES(CONV(?, 16, 10), ?, ?, COALESCE(?, $now), COALESCE(?, $now)) + ON DUPLICATE KEY UPDATE + first_seen = IF( + first_seen IS NULL, + COALESCE(?, $now), + LEAST(first_seen, COALESCE(?, $now))), + last_seen = IF( + last_seen IS NULL, + COALESCE(?, $now), + GREATEST(last_seen, COALESCE(?, $now))) + SQL + MKDEBUG && _d('SQL to insert into review table:', $sql); + my $insert_sth = $args{dbh}->prepare($sql); + + my @review_cols = grep { !$skip_cols{$_} } @{$args{tbl_struct}->{cols}}; + $sql = "SELECT " + . join(', ', map { $args{quoter}->quote($_) } @review_cols) + . ", CONV(checksum, 10, 16) AS checksum_conv FROM $args{db_tbl}" + . " WHERE checksum=CONV(?, 16, 10)"; + MKDEBUG && _d('SQL to select from review table:', $sql); + my $select_sth = $args{dbh}->prepare($sql); + + my $self = { + dbh => $args{dbh}, + db_tbl => $args{db_tbl}, + insert_sth => $insert_sth, + select_sth => $select_sth, + tbl_struct => $args{tbl_struct}, + quoter => $args{quoter}, + ts_default => $now, + }; + return bless $self, $class; +} + +sub set_history_options { + my ( $self, %args ) = @_; + foreach my $arg ( qw(table dbh tbl_struct col_pat) ) { + die "I need a $arg argument" unless $args{$arg}; + } + + my @cols; + my @metrics; + foreach my $col ( @{$args{tbl_struct}->{cols}} ) { + my ( $attr, $metric ) = $col =~ m/$args{col_pat}/; + next unless $attr && $metric; + $attr = ucfirst $attr if $attr =~ m/_/; # TableParser lowercases + push @cols, $col; + push @metrics, [$attr, $metric]; + } + + my $sql = "REPLACE INTO $args{table}(" + . join(', ', + map { $self->{quoter}->quote($_) } ('checksum', 'sample', @cols)) + . ') VALUES (CONV(?, 16, 10), ?, ' + . join(', ', map { + $_ eq 'ts_min' || $_ eq 'ts_max' + ? "COALESCE(?, $self->{ts_default})" + : '?' + } @cols) . ')'; + MKDEBUG && _d($sql); + + $self->{history_sth} = $args{dbh}->prepare($sql); + $self->{history_cols} = \@cols; + $self->{history_metrics} = \@metrics; +} + +sub set_review_history { + my ( $self, $id, $sample, %data ) = @_; + foreach my $thing ( qw(min max) ) { + next unless defined $data{ts} && defined $data{ts}->{$thing}; + $data{ts}->{$thing} = parse_timestamp($data{ts}->{$thing}); + } + $self->{history_sth}->execute( + make_checksum($id), + $sample, + map { $data{$_->[0]}->{$_->[1]} } @{$self->{history_metrics}}); +} + +sub get_review_info { + my ( $self, $id ) = @_; + $self->{select_sth}->execute(make_checksum($id)); + my $review_vals = $self->{select_sth}->fetchall_arrayref({}); + if ( $review_vals && @$review_vals == 1 ) { + return $review_vals->[0]; + } + return undef; +} + +sub set_review_info { + my ( $self, %args ) = @_; + $self->{insert_sth}->execute( + make_checksum($args{fingerprint}), + @args{qw(fingerprint sample)}, + map { $args{$_} ? parse_timestamp($args{$_}) : undef } + qw(first_seen last_seen first_seen first_seen last_seen last_seen)); +} + +sub review_cols { + my ( $self ) = @_; + return grep { !$skip_cols{$_} } @{$self->{tbl_struct}->{cols}}; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; +# ########################################################################### +# End QueryReview package +# ########################################################################### + +# ########################################################################### +# Daemon package 4565 +# ########################################################################### + +package Daemon; + +use strict; +use warnings FATAL => 'all'; + +use POSIX qw(setsid); +use English qw(-no_match_vars); + +use constant MKDEBUG => $ENV{MKDEBUG}; + +sub new { + my ( $class, %args ) = @_; + foreach my $arg ( qw(o) ) { + die "I need a $arg argument" unless $args{$arg}; + } + my $o = $args{o}; + my $self = { + o => $o, + log_file => $o->has('log') ? $o->get('log') : undef, + PID_file => $o->has('pid') ? $o->get('pid') : undef, + }; + + check_PID_file(undef, $self->{PID_file}); + + MKDEBUG && _d('Daemonized child will log to', $self->{log_file}); + return bless $self, $class; +} + +sub daemonize { + my ( $self ) = @_; + + MKDEBUG && _d('About to fork and daemonize'); + defined (my $pid = fork()) or die "Cannot fork: $OS_ERROR"; + if ( $pid ) { + MKDEBUG && _d('I am the parent and now I die'); + exit; + } + + $self->{child} = 1; + + POSIX::setsid() or die "Cannot start a new session: $OS_ERROR"; + chdir '/' or die "Cannot chdir to /: $OS_ERROR"; + + $self->_make_PID_file(); + + $OUTPUT_AUTOFLUSH = 1; + + if ( -t STDIN ) { + close STDIN; + open STDIN, '/dev/null' + or die "Cannot reopen STDIN to /dev/null: $OS_ERROR"; + } + + if ( $self->{log_file} ) { + close STDOUT; + open STDOUT, '>>', $self->{log_file} + or die "Cannot open log file $self->{log_file}: $OS_ERROR"; + + close STDERR; + open STDERR, ">&STDOUT" + or die "Cannot dupe STDERR to STDOUT: $OS_ERROR"; + } + else { + if ( -t STDOUT ) { + close STDOUT; + open STDOUT, '>', '/dev/null' + or die "Cannot reopen STDOUT to /dev/null: $OS_ERROR"; + } + if ( -t STDERR ) { + close STDERR; + open STDERR, '>', '/dev/null' + or die "Cannot reopen STDERR to /dev/null: $OS_ERROR"; + } + } + + MKDEBUG && _d('I am the child and now I live daemonized'); + return; +} + +sub check_PID_file { + my ( $self, $file ) = @_; + my $PID_file = $self ? $self->{PID_file} : $file; + MKDEBUG && _d('Checking PID file', $PID_file); + if ( $PID_file && -f $PID_file ) { + my $pid; + eval { chomp($pid = `cat $PID_file`); }; + die "Cannot cat $PID_file: $OS_ERROR" if $EVAL_ERROR; + MKDEBUG && _d('PID file exists; it contains PID', $pid); + if ( $pid ) { + my $pid_is_alive = kill 0, $pid; + if ( $pid_is_alive ) { + die "The PID file $PID_file already exists " + . " and the PID that it contains, $pid, is running"; + } + else { + warn "Overwriting PID file $PID_file because the PID that it " + . "contains, $pid, is not running"; + } + } + else { + die "The PID file $PID_file already exists but it does not " + . "contain a PID"; + } + } + else { + MKDEBUG && _d('No PID file'); + } + return; +} + +sub make_PID_file { + my ( $self ) = @_; + if ( exists $self->{child} ) { + die "Do not call Daemon::make_PID_file() for daemonized scripts"; + } + $self->_make_PID_file(); + $self->{rm_PID_file} = 1; + return; +} + +sub _make_PID_file { + my ( $self ) = @_; + + my $PID_file = $self->{PID_file}; + if ( !$PID_file ) { + MKDEBUG && _d('No PID file to create'); + return; + } + + $self->check_PID_file(); + + open my $PID_FH, '>', $PID_file + or die "Cannot open PID file $PID_file: $OS_ERROR"; + print $PID_FH $PID + or die "Cannot print to PID file $PID_file: $OS_ERROR"; + close $PID_FH + or die "Cannot close PID file $PID_file: $OS_ERROR"; + + MKDEBUG && _d('Created PID file:', $self->{PID_file}); + return; +} + +sub _remove_PID_file { + my ( $self ) = @_; + if ( $self->{PID_file} && -f $self->{PID_file} ) { + unlink $self->{PID_file} + or warn "Cannot remove PID file $self->{PID_file}: $OS_ERROR"; + MKDEBUG && _d('Removed PID file'); + } + else { + MKDEBUG && _d('No PID to remove'); + } + return; +} + +sub DESTROY { + my ( $self ) = @_; + $self->_remove_PID_file() if $self->{child} || $self->{rm_PID_file}; + return; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End Daemon package +# ########################################################################### + +# ########################################################################### +# MemcachedProtocolParser package 5130 +# ########################################################################### +package MemcachedProtocolParser; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); + +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; +$Data::Dumper::Quotekeys = 0; + +use constant MKDEBUG => $ENV{MKDEBUG}; + +sub new { + my ( $class, %args ) = @_; + + my ( $server_port ) + = $args{server} ? $args{server} =~ m/:(\w+)/ : ('11211'); + $server_port ||= '11211'; # In case $args{server} doesn't have a port. + + my $self = { + server => $args{server}, + server_port => $server_port, + sessions => {}, + o => $args{o}, + }; + return bless $self, $class; +} + +sub parse_event { + my ( $self, %args ) = @_; + my @required_args = qw(event); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my $packet = @args{@required_args}; + + my $src_host = "$packet->{src_host}:$packet->{src_port}"; + my $dst_host = "$packet->{dst_host}:$packet->{dst_port}"; + + if ( my $server = $self->{server} ) { # Watch only the given server. + if ( $src_host ne $server && $dst_host ne $server ) { + MKDEBUG && _d('Packet is not to or from', $server); + return; + } + } + + my $packet_from; + my $client; + if ( $src_host =~ m/:$self->{server_port}$/ ) { + $packet_from = 'server'; + $client = $dst_host; + } + elsif ( $dst_host =~ m/:$self->{server_port}$/ ) { + $packet_from = 'client'; + $client = $src_host; + } + else { + warn 'Packet is not to or from memcached server: ', Dumper($packet); + return; + } + MKDEBUG && _d('Client:', $client); + + if ( !exists $self->{sessions}->{$client} ) { + MKDEBUG && _d('New session'); + $self->{sessions}->{$client} = { + client => $client, + state => undef, + raw_packets => [], + }; + }; + my $session = $self->{sessions}->{$client}; + + if ( $packet->{data_len} == 0 ) { + MKDEBUG && _d('No TCP data'); + return; + } + + push @{$session->{raw_packets}}, $packet->{raw_packet}; + + $packet->{data} = pack('H*', $packet->{data}); + my $event; + if ( $packet_from eq 'server' ) { + $event = $self->_packet_from_server($packet, $session, $args{misc}); + } + elsif ( $packet_from eq 'client' ) { + $event = $self->_packet_from_client($packet, $session, $args{misc}); + } + else { + die 'Packet origin unknown'; + } + + MKDEBUG && _d('Done with packet; event:', Dumper($event)); + return $event; +} + +sub _packet_from_server { + my ( $self, $packet, $session, $misc ) = @_; + die "I need a packet" unless $packet; + die "I need a session" unless $session; + + MKDEBUG && _d('Packet is from server; client state:', $session->{state}); + + my $data = $packet->{data}; + + if ( !$session->{state} ) { + MKDEBUG && _d('Ignoring mid-stream server response'); + return; + } + + if ( $session->{state} eq 'awaiting reply' ) { + MKDEBUG && _d('State is awaiting reply'); + my ($line1, $rest) = $packet->{data} =~ m/\A(.*?)\r\n(.*)?/s; + + my @vals = $line1 =~ m/(\S+)/g; + $session->{res} = shift @vals; + MKDEBUG && _d('Result of last', $session->{cmd}, 'cmd:', $session->{res}); + + if ( $session->{cmd} eq 'incr' || $session->{cmd} eq 'decr' ) { + MKDEBUG && _d('It is an incr or decr'); + if ( $session->{res} !~ m/\D/ ) { # It's an integer, not an error + MKDEBUG && _d('Got a value for the incr/decr'); + $session->{val} = $session->{res}; + $session->{res} = ''; + } + } + elsif ( $session->{res} eq 'VALUE' ) { + MKDEBUG && _d('It is the result of a "get"'); + my ($key, $flags, $bytes) = @vals; + defined $session->{flags} or $session->{flags} = $flags; + defined $session->{bytes} or $session->{bytes} = $bytes; + if ( $rest && $bytes ) { + MKDEBUG && _d('There is a value'); + if ( length($rest) > $bytes ) { + MKDEBUG && _d('Looks like we got the whole response'); + $session->{val} = substr($rest, 0, $bytes); # Got the whole response. + } + else { + MKDEBUG && _d('Got partial response, saving for later'); + push @{$session->{partial}}, [ $packet->{seq}, $rest ]; + $session->{gathered} += length($rest); + $session->{state} = 'partial recv'; + return; # Prevent firing an event. + } + } + } + elsif ( $session->{res} eq 'END' ) { + MKDEBUG && _d('Got an END without any data, firing NOT_FOUND'); + $session->{res} = 'NOT_FOUND'; + } + elsif ( $session->{res} !~ m/STORED|DELETED|NOT_FOUND/ ) { + MKDEBUG && _d('Unknown result'); + } + } + else { # Should be 'partial recv' + MKDEBUG && _d('Session state: ', $session->{state}); + push @{$session->{partial}}, [ $packet->{seq}, $data ]; + $session->{gathered} += length($data); + MKDEBUG && _d('Gathered', $session->{gathered}, 'bytes in', + scalar(@{$session->{partial}}), 'packets from server'); + if ( $session->{gathered} >= $session->{bytes} + 2 ) { # Done. + MKDEBUG && _d('End of partial response, preparing event'); + my $val = join('', + map { $_->[1] } + sort { $a->[0] <=> $b->[0] } + @{$session->{partial}}); + $session->{val} = substr($val, 0, $session->{bytes}); + } + else { + MKDEBUG && _d('Partial response continues, no action'); + return; # Prevent firing event. + } + } + + MKDEBUG && _d('Creating event, deleting session'); + my $event = make_event($session, $packet); + delete $self->{sessions}->{$session->{client}}; # memcached is stateless! + $session->{raw_packets} = []; # Avoid keeping forever + return $event; +} + +sub _packet_from_client { + my ( $self, $packet, $session, $misc ) = @_; + die "I need a packet" unless $packet; + die "I need a session" unless $session; + + MKDEBUG && _d('Packet is from client; state:', $session->{state}); + + my $event; + if ( ($session->{state} || '') =~m/awaiting reply|partial recv/ ) { + MKDEBUG && _d("Expected data from the client, looks like interrupted"); + $session->{res} = 'INTERRUPTED'; + $event = make_event($session, $packet); + my $client = $session->{client}; + delete @{$session}{keys %$session}; + $session->{client} = $client; + } + + my ($line1, $val); + my ($cmd, $key, $flags, $exptime, $bytes); + + if ( !$session->{state} ) { + MKDEBUG && _d('Session state: ', $session->{state}); + ($line1, $val) = $packet->{data} =~ m/\A(.*?)\r\n(.+)?/s; + my @vals = $line1 =~ m/(\S+)/g; + $cmd = lc shift @vals; + MKDEBUG && _d('$cmd is a ', $cmd); + if ( $cmd eq 'set' || $cmd eq 'add' ) { + ($key, $flags, $exptime, $bytes) = @vals; + $session->{bytes} = $bytes; + } + elsif ( $cmd eq 'get' ) { + ($key) = @vals; + if ( $val ) { + MKDEBUG && _d('Multiple cmds:', $val); + $val = undef; + } + } + elsif ( $cmd eq 'delete' ) { + ($key) = @vals; # TODO: handle the + if ( $val ) { + MKDEBUG && _d('Multiple cmds:', $val); + $val = undef; + } + } + elsif ( $cmd eq 'incr' || $cmd eq 'decr' ) { + ($key) = @vals; + } + else { + MKDEBUG && _d("Don't know how to handle", $cmd, "command"); + } + @{$session}{qw(cmd key flags exptime)} + = ($cmd, $key, $flags, $exptime); + $session->{host} = $packet->{src_host}; + $session->{pos_in_log} = $packet->{pos_in_log}; + $session->{ts} = $packet->{ts}; + } + else { + MKDEBUG && _d('Session state: ', $session->{state}); + $val = $packet->{data}; + } + + $session->{state} = 'awaiting reply'; # Assume we got the whole packet + if ( $val ) { + if ( $session->{bytes} + 2 == length($val) ) { # +2 for the \r\n + MKDEBUG && _d('Got the whole thing'); + $val =~ s/\r\n\Z//; # We got the whole thing. + $session->{val} = $val; + } + else { # We apparently did NOT get the whole thing. + MKDEBUG && _d('Partial send, saving for later'); + push @{$session->{partial}}, + [ $packet->{seq}, $val ]; + $session->{gathered} += length($val); + MKDEBUG && _d('Gathered', $session->{gathered}, 'bytes in', + scalar(@{$session->{partial}}), 'packets from client'); + if ( $session->{gathered} >= $session->{bytes} + 2 ) { # Done. + MKDEBUG && _d('Message looks complete now, saving value'); + $val = join('', + map { $_->[1] } + sort { $a->[0] <=> $b->[0] } + @{$session->{partial}}); + $val =~ s/\r\n\Z//; + $session->{val} = $val; + } + else { + MKDEBUG && _d('Message not complete'); + $val = '[INCOMPLETE]'; + $session->{state} = 'partial send'; + } + } + } + + return $event; +} + +sub make_event { + my ( $session, $packet ) = @_; + my $event = { + cmd => $session->{cmd}, + key => $session->{key}, + val => $session->{val} || '', + res => $session->{res}, + ts => $session->{ts}, + host => $session->{host}, + flags => $session->{flags} || 0, + exptime => $session->{exptime} || 0, + bytes => $session->{bytes} || 0, + Query_time => timestamp_diff($session->{ts}, $packet->{ts}), + pos_in_log => $session->{pos_in_log}, + }; + return $event; +} + +sub _get_errors_fh { + my ( $self ) = @_; + my $errors_fh = $self->{errors_fh}; + return $errors_fh if $errors_fh; + + my $o = $self->{o}; + if ( $o && $o->has('tcpdump-errors') && $o->got('tcpdump-errors') ) { + my $errors_file = $o->get('tcpdump-errors'); + MKDEBUG && _d('tcpdump-errors file:', $errors_file); + open $errors_fh, '>>', $errors_file + or die "Cannot open tcpdump-errors file $errors_file: $OS_ERROR"; + } + + $self->{errors_fh} = $errors_fh; + return $errors_fh; +} + +sub fail_session { + my ( $self, $session, $reason ) = @_; + my $errors_fh = $self->_get_errors_fh(); + if ( $errors_fh ) { + $session->{reason_for_failure} = $reason; + my $session_dump = '# ' . Dumper($session); + chomp $session_dump; + $session_dump =~ s/\n/\n# /g; + print $errors_fh "$session_dump\n"; + { + local $LIST_SEPARATOR = "\n"; + print $errors_fh "@{$session->{raw_packets}}"; + print $errors_fh "\n"; + } + } + MKDEBUG && _d('Failed session', $session->{client}, 'because', $reason); + delete $self->{sessions}->{$session->{client}}; + return; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +sub timestamp_diff { + my ( $start, $end ) = @_; + my $sd = substr($start, 0, 11, ''); + my $ed = substr($end, 0, 11, ''); + my ( $sh, $sm, $ss ) = split(/:/, $start); + my ( $eh, $em, $es ) = split(/:/, $end); + my $esecs = ($eh * 3600 + $em * 60 + $es); + my $ssecs = ($sh * 3600 + $sm * 60 + $ss); + if ( $sd eq $ed ) { + return sprintf '%.6f', $esecs - $ssecs; + } + else { # Assume only one day boundary has been crossed, no DST, etc + return sprintf '%.6f', ( 86_400 - $ssecs ) + $esecs; + } +} + +1; + +# ########################################################################### +# End MemcachedProtocolParser package +# ########################################################################### + +# ########################################################################### +# MemcachedEvent package 5139 +# ########################################################################### +package MemcachedEvent; + + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); + +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; +$Data::Dumper::Quotekeys = 0; + +use constant MKDEBUG => $ENV{MKDEBUG}; + +my %cmds = map { $_ => 1 } qw( + set + add + replace + append + prepend + cas + get + gets + delete + incr + decr +); + +my %cmd_handler_for = ( + set => \&handle_storage_cmd, + add => \&handle_storage_cmd, + replace => \&handle_storage_cmd, + append => \&handle_storage_cmd, + prepend => \&handle_storage_cmd, + cas => \&handle_storage_cmd, + get => \&handle_retr_cmd, + gets => \&handle_retr_cmd, +); + +sub new { + my ( $class, %args ) = @_; + my $self = {}; + return bless $self, $class; +} + +sub parse_event { + my ( $self, %args ) = @_; + my $event = $args{event}; + return unless $event; + + if ( !$event->{cmd} || !$event->{key} ) { + MKDEBUG && _d('Event has no cmd or key:', Dumper($event)); + return; + } + + if ( !$cmds{$event->{cmd}} ) { + MKDEBUG && _d("Don't know how to handle cmd:", $event->{cmd}); + return; + } + + $event->{arg} = "$event->{cmd} $event->{key}"; + $event->{fingerprint} = $self->fingerprint($event->{arg}); + $event->{key_print} = $self->fingerprint($event->{key}); + + map { $event->{"Memc_$_"} = 'No' } keys %cmds; + $event->{"Memc_$event->{cmd}"} = 'Yes'; # Got this cmd. + $event->{Memc_error} = 'No'; # A handler may change this. + $event->{Memc_miss} = 'No'; + if ( $event->{res} ) { + $event->{Memc_miss} = 'Yes' if $event->{res} eq 'NOT_FOUND'; + } + else { + MKDEBUG && _d('Event has no res:', Dumper($event)); + } + + if ( $cmd_handler_for{$event->{cmd}} ) { + return $cmd_handler_for{$event->{cmd}}->($event); + } + + return $event; +} + +sub fingerprint { + my ( $self, $val ) = @_; + $val =~ s/[0-9A-Fa-f]{16,}|\d+/?/g; + return $val; +} + +sub handle_storage_cmd { + my ( $event ) = @_; + + if ( !$event->{res} ) { + MKDEBUG && _d('No result for event:', Dumper($event)); + return; + } + + $event->{'Memc_Not_Stored'} = $event->{res} eq 'NOT_STORED' ? 'Yes' : 'No'; + $event->{'Memc_Exists'} = $event->{res} eq 'EXISTS' ? 'Yes' : 'No'; + + return $event; +} + +sub handle_retr_cmd { + my ( $event ) = @_; + + if ( !$event->{res} ) { + MKDEBUG && _d('No result for event:', Dumper($event)); + return; + } + + $event->{'Memc_error'} = $event->{res} eq 'INTERRUPTED' ? 'Yes' : 'No'; + + return $event; +} + + +sub handle_delete { + my ( $event ) = @_; + return $event; +} + +sub handle_incr_decr_cmd { + my ( $event ) = @_; + return $event; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End MemcachedEvent package +# ########################################################################### + +# ########################################################################### +# BinaryLogParser package 5134 +# ########################################################################### +package BinaryLogParser; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); + +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; +$Data::Dumper::Quotekeys = 0; + +use constant MKDEBUG => $ENV{MKDEBUG}; + +sub new { + my ( $class, %args ) = @_; + my $self = { + delim => undef, + delim_len => 0, + }; + return bless {}, $class; +} + +my $binlog_line_1 = qr/at (\d+)$/m; +my $binlog_line_2 = qr/^#(\d{6}\s+\d{1,2}:\d\d:\d\d)\s+server\s+id\s+(\d+)\s+end_log_pos\s+(\d+)\s+(\S+)\s*([^\n]*)$/m; +my $binlog_line_2_rest = qr/thread_id=(\d+)\s+exec_time=(\d+)\s+error_code=(\d+)/m; + +sub parse_event { + my ( $self, %args ) = @_; + my @required_args = qw(fh); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my $fh = @args{@required_args}; + + local $INPUT_RECORD_SEPARATOR = ";\n#"; + my $pos_in_log = tell($fh); + my $stmt; + my ($delim, $delim_len) = ($self->{delim}, $self->{delim_len}); + + EVENT: + while ( defined($stmt = <$fh>) ) { + my @properties = ('pos_in_log', $pos_in_log); + my ($ts, $sid, $end, $type, $rest); + $pos_in_log = tell($fh); + $stmt =~ s/;\n#?\Z//; + + my ( $got_offset, $got_hdr ); + my $pos = 0; + my $len = length($stmt); + my $found_arg = 0; + LINE: + while ( $stmt =~ m/^(.*)$/mg ) { # /g requires scalar match. + $pos = pos($stmt); # Be careful not to mess this up! + my $line = $1; # Necessary for /g and pos() to work. + $line =~ s/$delim// if $delim; + MKDEBUG && _d($line); + + if ( $line =~ m/^\/\*.+\*\/;/ ) { + MKDEBUG && _d('Comment line'); + next LINE; + } + + if ( $line =~ m/^DELIMITER/m ) { + my ( $del ) = $line =~ m/^DELIMITER (\S*)$/m; + if ( $del ) { + $self->{delim_len} = $delim_len = length $del; + $self->{delim} = $delim = quotemeta $del; + MKDEBUG && _d('delimiter:', $delim); + } + else { + MKDEBUG && _d('Delimiter reset to ;'); + $self->{delim} = $delim = undef; + $self->{delim_len} = $delim_len = 0; + } + next LINE; + } + + next LINE if $line =~ m/End of log file/; + + if ( !$got_offset && (my ( $offset ) = $line =~ m/$binlog_line_1/m) ) { + MKDEBUG && _d('Got the at offset line'); + push @properties, 'offset', $offset; + $got_offset++; + } + + elsif ( !$got_hdr && $line =~ m/^#(\d{6}\s+\d{1,2}:\d\d:\d\d)/ ) { + ($ts, $sid, $end, $type, $rest) = $line =~ m/$binlog_line_2/m; + MKDEBUG && _d('Got the header line; type:', $type, 'rest:', $rest); + push @properties, 'cmd', 'Query', 'ts', $ts, 'server_id', $sid, + 'end_log_pos', $end; + $got_hdr++; + } + + elsif ( $line =~ m/^(?:#|use |SET)/i ) { + + if ( my ( $db ) = $line =~ m/^use ([^;]+)/ ) { + MKDEBUG && _d("Got a default database:", $db); + push @properties, 'db', $db; + } + + elsif ( my ($setting) = $line =~ m/^SET\s+([^;]*)/ ) { + MKDEBUG && _d("Got some setting:", $setting); + push @properties, map { s/\s+//; lc } split(/,|\s*=\s*/, $setting); + } + + } + else { + MKDEBUG && _d("Got the query/arg line at pos", $pos); + $found_arg++; + if ( $got_offset && $got_hdr ) { + if ( $type eq 'Xid' ) { + my ($xid) = $rest =~ m/(\d+)/; + push @properties, 'Xid', $xid; + } + elsif ( $type eq 'Query' ) { + my ($i, $t, $c) = $rest =~ m/$binlog_line_2_rest/m; + push @properties, 'Thread_id', $i, 'Query_time', $t, + 'error_code', $c; + } + elsif ( $type eq 'Start:' ) { + MKDEBUG && _d("Binlog start"); + } + else { + MKDEBUG && _d('Unknown event type:', $type); + next EVENT; + } + } + else { + MKDEBUG && _d("It's not a query/arg, it's just some SQL fluff"); + push @properties, 'cmd', 'Query', 'ts', undef; + } + + my $delim_len = ($pos == length($stmt) ? $delim_len : 0); + my $arg = substr($stmt, $pos - length($line) - $delim_len); + + $arg =~ s/$delim// if $delim; # Remove the delimiter. + + if ( $arg =~ m/^DELIMITER/m ) { + my ( $del ) = $arg =~ m/^DELIMITER (\S*)$/m; + if ( $del ) { + $self->{delim_len} = $delim_len = length $del; + $self->{delim} = $delim = quotemeta $del; + MKDEBUG && _d('delimiter:', $delim); + } + else { + MKDEBUG && _d('Delimiter reset to ;'); + $del = ';'; + $self->{delim} = $delim = undef; + $self->{delim_len} = $delim_len = 0; + } + + $arg =~ s/^DELIMITER.*$//m; # Remove DELIMITER from arg. + } + + $arg =~ s/;$//gm; # Ensure ending ; are gone. + $arg =~ s/\s+$//; # Remove trailing spaces and newlines. + + push @properties, 'arg', $arg, 'bytes', length($arg); + last LINE; + } + } # LINE + + if ( $found_arg ) { + MKDEBUG && _d('Properties of event:', Dumper(\@properties)); + my $event = { @properties }; + return $event; + } + else { + MKDEBUG && _d('Event had no arg'); + } + } # EVENT + + $args{oktorun}->(0) if $args{oktorun}; + return; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End BinaryLogParser package +# ########################################################################### + +# ########################################################################### +# GeneralLogParser package 5135 +# ########################################################################### +package GeneralLogParser; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); + +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; +$Data::Dumper::Quotekeys = 0; + +use constant MKDEBUG => $ENV{MKDEBUG}; + +sub new { + my ( $class ) = @_; + my $self = { + pending => [], + db_for => {}, + }; + return bless $self, $class; +} + +my $genlog_line_1= qr{ + \A + (?:(\d{6}\s\d{1,2}:\d\d:\d\d))? # Timestamp + \s+ + (?:\s*(\d+)) # Thread ID + \s + (\w+) # Command + \s+ + (.*) # Argument + \Z +}xs; + +sub parse_event { + my ( $self, %args ) = @_; + my @required_args = qw(fh); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my $fh = @args{@required_args}; + + my $pending = $self->{pending}; + my $db_for = $self->{db_for}; + my $line; + my $pos_in_log = tell($fh); + LINE: + while ( (defined($line = shift @$pending) or defined($line = <$fh>)) ) { + MKDEBUG && _d($line); + my ($ts, $thread_id, $cmd, $arg) = $line =~ m/$genlog_line_1/; + if ( !($thread_id && $cmd) ) { + MKDEBUG && _d('Not start of general log event'); + next; + } + my @properties = ('pos_in_log', $pos_in_log, 'ts', $ts, + 'Thread_id', $thread_id); + + $pos_in_log = tell($fh); + + @$pending = (); + if ( $cmd eq 'Query' ) { + my $done = 0; + do { + $line = <$fh>; + if ( $line ) { + ($ts, $thread_id, $cmd, undef) = $line =~ m/$genlog_line_1/; + if ( $thread_id && $cmd ) { + MKDEBUG && _d('Event done'); + $done = 1; + push @$pending, $line; + } + else { + MKDEBUG && _d('More arg:', $line); + $arg .= $line; + } + } + else { + MKDEBUG && _d('No more lines'); + $done = 1; + } + } until ( $done ); + + chomp $arg; + push @properties, 'cmd', 'Query', 'arg', $arg; + push @properties, 'bytes', length($properties[-1]); + push @properties, 'db', $db_for->{$thread_id} if $db_for->{$thread_id}; + } + else { + push @properties, 'cmd', 'Admin'; + + if ( $cmd eq 'Connect' ) { + if ( $arg =~ m/^Access denied/ ) { + $cmd = $arg; + } + else { + my ($user, undef, $db) = $arg =~ /(\S+)/g; + my $host; + ($user, $host) = split(/@/, $user); + MKDEBUG && _d('Connect', $user, '@', $host, 'on', $db); + + push @properties, 'user', $user if $user; + push @properties, 'host', $host if $host; + push @properties, 'db', $db if $db; + $db_for->{$thread_id} = $db; + } + } + elsif ( $cmd eq 'Init' ) { + $cmd = 'Init DB'; + $arg =~ s/^DB\s+//; + my ($db) = $arg =~ /(\S+)/; + MKDEBUG && _d('Init DB:', $db); + push @properties, 'db', $db if $db; + $db_for->{$thread_id} = $db; + } + + push @properties, 'arg', "administrator command: $cmd"; + push @properties, 'bytes', length($properties[-1]); + } + + push @properties, 'Query_time', 0; + + MKDEBUG && _d('Properties of event:', Dumper(\@properties)); + my $event = { @properties }; + return $event; + } # LINE + + @{$self->{pending}} = (); + $args{oktorun}->(0) if $args{oktorun}; + return; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End GeneralLogParser package +# ########################################################################### + +# ########################################################################### +# ProtocolParser package 5129 +# ########################################################################### +package ProtocolParser; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); + +eval { + require IO::Uncompress::Inflate; + IO::Uncompress::Inflate->import(qw(inflate $InflateError)); +}; + +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; +$Data::Dumper::Quotekeys = 0; + +use constant MKDEBUG => $ENV{MKDEBUG}; + +sub new { + my ( $class, %args ) = @_; + + my ($server_port) = $args{server} =~ m/:(\w+)/ if $args{server}; + $server_port = $args{server_port} if !$server_port && $args{server_port}; + + my $self = { + server => $args{server}, + server_port => $server_port, + sessions => {}, + o => $args{o}, + }; + + return bless $self, $class; +} + +sub parse_event { + my ( $self, %args ) = @_; + my @required_args = qw(event); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my $packet = @args{@required_args}; + + if ( $self->{buffer} ) { + my ($packet_from, $session) = $self->_get_session($packet); + if ( $packet->{data_len} ) { + if ( $packet_from eq 'client' ) { + push @{$session->{client_packets}}, $packet; + MKDEBUG && _d('Saved client packet'); + } + else { + push @{$session->{server_packets}}, $packet; + MKDEBUG && _d('Saved server packet'); + } + } + + return unless ($packet_from eq 'client') + && ($packet->{fin} || $packet->{rst}); + + my $event; + map { + $event = $self->_parse_packet($_, $args{misc}); + } sort { $a->{seq} <=> $b->{seq} } + @{$session->{client_packets}}; + + map { + $event = $self->_parse_packet($_, $args{misc}); + } sort { $a->{seq} <=> $b->{seq} } + @{$session->{server_packets}}; + + return $event; + } + + if ( $packet->{data_len} == 0 ) { + MKDEBUG && _d('No TCP data'); + return; + } + + return $self->_parse_packet($packet, $args{misc}); +} + +sub _parse_packet { + my ( $self, $packet, $misc ) = @_; + + my ($packet_from, $session) = $self->_get_session($packet); + MKDEBUG && _d('State:', $session->{state}); + + push @{$session->{raw_packets}}, $packet->{raw_packet} + unless $misc->{recurse}; + + if ( $session->{buff} ) { + $session->{buff_left} -= $packet->{data_len}; + if ( $session->{buff_left} > 0 ) { + MKDEBUG && _d('Added data to buff; expecting', $session->{buff_left}, + 'more bytes'); + return; + } + + MKDEBUG && _d('Got all data; buff left:', $session->{buff_left}); + $packet->{data} = $session->{buff} . $packet->{data}; + $packet->{data_len} += length $session->{buff}; + $session->{buff} = ''; + $session->{buff_left} = 0; + } + + $packet->{data} = pack('H*', $packet->{data}) unless $misc->{recurse}; + my $event; + if ( $packet_from eq 'server' ) { + $event = $self->_packet_from_server($packet, $session, $misc); + } + elsif ( $packet_from eq 'client' ) { + $event = $self->_packet_from_client($packet, $session, $misc); + } + else { + die 'Packet origin unknown'; + } + MKDEBUG && _d('State:', $session->{state}); + + if ( $session->{out_of_order} ) { + MKDEBUG && _d('Session packets are out of order'); + push @{$session->{packets}}, $packet; + $session->{ts_min} + = $packet->{ts} if $packet->{ts} lt ($session->{ts_min} || ''); + $session->{ts_max} + = $packet->{ts} if $packet->{ts} gt ($session->{ts_max} || ''); + if ( $session->{have_all_packets} ) { + MKDEBUG && _d('Have all packets; ordering and processing'); + delete $session->{out_of_order}; + delete $session->{have_all_packets}; + map { + $event = $self->_parse_packet($_, { recurse => 1 }); + } sort { $a->{seq} <=> $b->{seq} } @{$session->{packets}}; + } + } + + MKDEBUG && _d('Done with packet; event:', Dumper($event)); + return $event; +} + +sub _get_session { + my ( $self, $packet ) = @_; + + my $src_host = "$packet->{src_host}:$packet->{src_port}"; + my $dst_host = "$packet->{dst_host}:$packet->{dst_port}"; + + if ( my $server = $self->{server} ) { # Watch only the given server. + if ( $src_host ne $server && $dst_host ne $server ) { + MKDEBUG && _d('Packet is not to or from', $server); + return; + } + } + + my $packet_from; + my $client; + if ( $src_host =~ m/:$self->{server_port}$/ ) { + $packet_from = 'server'; + $client = $dst_host; + } + elsif ( $dst_host =~ m/:$self->{server_port}$/ ) { + $packet_from = 'client'; + $client = $src_host; + } + else { + warn 'Packet is not to or from server: ', Dumper($packet); + return; + } + MKDEBUG && _d('Client:', $client); + + if ( !exists $self->{sessions}->{$client} ) { + MKDEBUG && _d('New session'); + $self->{sessions}->{$client} = { + client => $client, + state => undef, + raw_packets => [], + }; + }; + my $session = $self->{sessions}->{$client}; + + return $packet_from, $session; +} + +sub _packet_from_server { + die "Don't call parent class _packet_from_server()"; +} + +sub _packet_from_client { + die "Don't call parent class _packet_from_client()"; +} + +sub make_event { + my ( $self, $session, $packet ) = @_; + die "Event has no attributes" unless scalar keys %{$session->{attribs}}; + die "Query has no arg attribute" unless $session->{attribs}->{arg}; + my $start_request = $session->{start_request} || 0; + my $start_reply = $session->{start_reply} || 0; + my $end_reply = $session->{end_reply} || 0; + MKDEBUG && _d('Request start:', $start_request, + 'reply start:', $start_reply, 'reply end:', $end_reply); + my $event = { + Query_time => $self->timestamp_diff($start_request, $start_reply), + Transmit_time => $self->timestamp_diff($start_reply, $end_reply), + }; + @{$event}{keys %{$session->{attribs}}} = values %{$session->{attribs}}; + return $event; +} + +sub _get_errors_fh { + my ( $self ) = @_; + my $errors_fh = $self->{errors_fh}; + return $errors_fh if $errors_fh; + + my $o = $self->{o}; + if ( $o && $o->has('tcpdump-errors') && $o->got('tcpdump-errors') ) { + my $errors_file = $o->get('tcpdump-errors'); + MKDEBUG && _d('tcpdump-errors file:', $errors_file); + open $errors_fh, '>>', $errors_file + or die "Cannot open tcpdump-errors file $errors_file: $OS_ERROR"; + } + + $self->{errors_fh} = $errors_fh; + return $errors_fh; +} + +sub fail_session { + my ( $self, $session, $reason ) = @_; + my $errors_fh = $self->_get_errors_fh(); + if ( $errors_fh ) { + $session->{reason_for_failure} = $reason; + my $session_dump = '# ' . Dumper($session); + chomp $session_dump; + $session_dump =~ s/\n/\n# /g; + print $errors_fh "$session_dump\n"; + { + local $LIST_SEPARATOR = "\n"; + print $errors_fh "@{$session->{raw_packets}}"; + print $errors_fh "\n"; + } + } + MKDEBUG && _d('Failed session', $session->{client}, 'because', $reason); + delete $self->{sessions}->{$session->{client}}; + return; +} + +sub timestamp_diff { + my ( $self, $start, $end ) = @_; + return 0 unless $start && $end; + my $sd = substr($start, 0, 11, ''); + my $ed = substr($end, 0, 11, ''); + my ( $sh, $sm, $ss ) = split(/:/, $start); + my ( $eh, $em, $es ) = split(/:/, $end); + my $esecs = ($eh * 3600 + $em * 60 + $es); + my $ssecs = ($sh * 3600 + $sm * 60 + $ss); + if ( $sd eq $ed ) { + return sprintf '%.6f', $esecs - $ssecs; + } + else { # Assume only one day boundary has been crossed, no DST, etc + return sprintf '%.6f', ( 86_400 - $ssecs ) + $esecs; + } +} + +sub uncompress_data { + my ( $self, $data, $len ) = @_; + die "I need data" unless $data; + die "I need a len argument" unless $len; + die "I need a scalar reference to data" unless ref $data eq 'SCALAR'; + MKDEBUG && _d('Uncompressing data'); + our $InflateError; + + my $comp_bin_data = pack('H*', $$data); + + my $uncomp_bin_data = ''; + my $z = new IO::Uncompress::Inflate( + \$comp_bin_data + ) or die "IO::Uncompress::Inflate failed: $InflateError"; + my $status = $z->read(\$uncomp_bin_data, $len) + or die "IO::Uncompress::Inflate failed: $InflateError"; + + my $uncomp_data = unpack('H*', $uncomp_bin_data); + + return \$uncomp_data; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End ProtocolParser package +# ########################################################################### + +# ########################################################################### +# HTTPProtocolParser package 5121 +# ########################################################################### +package HTTPProtocolParser; +use base 'ProtocolParser'; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); + +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; +$Data::Dumper::Quotekeys = 0; + +use constant MKDEBUG => $ENV{MKDEBUG}; + +sub new { + my ( $class, %args ) = @_; + my $self = $class->SUPER::new( + %args, + server_port => 80, + ); + return $self; +} + +sub _packet_from_server { + my ( $self, $packet, $session, $misc ) = @_; + die "I need a packet" unless $packet; + die "I need a session" unless $session; + + MKDEBUG && _d('Packet is from server; client state:', $session->{state}); + + if ( !$session->{state} ) { + MKDEBUG && _d('Ignoring mid-stream server response'); + return; + } + + if ( $session->{out_of_order} ) { + my ($line1, $content); + if ( !$session->{have_header} ) { + ($line1, $content) = $self->_parse_header( + $session, $packet->{data}, $packet->{data_len}); + } + if ( $line1 ) { + $session->{have_header} = 1; + $packet->{content_len} = length $content; + MKDEBUG && _d('Got out of order header with', + $packet->{content_len}, 'bytes of content'); + } + my $have_len = $packet->{content_len} || $packet->{data_len}; + map { $have_len += $_->{data_len} } + @{$session->{packets}}; + $session->{have_all_packets} + = 1 if $session->{attribs}->{bytes} + && $have_len >= $session->{attribs}->{bytes}; + MKDEBUG && _d('Have', $have_len, 'of', $session->{attribs}->{bytes}); + return; + } + + if ( $session->{state} eq 'awaiting reply' ) { + + $session->{start_reply} = $packet->{ts} unless $session->{start_reply}; + + my ($line1, $content) = $self->_parse_header($session, $packet->{data}, + $packet->{data_len}); + + if ( !$line1 ) { + $session->{out_of_order} = 1; # alert parent + $session->{have_all_packets} = 0; + return; + } + + my ($version, $code, $phrase) = $line1 =~ m/(\S+)/g; + $session->{attribs}->{Status_code} = $code; + MKDEBUG && _d('Status code for last', $session->{attribs}->{arg}, + 'request:', $session->{attribs}->{Status_code}); + + my $content_len = $content ? length $content : 0; + MKDEBUG && _d('Got', $content_len, 'bytes of content'); + if ( $session->{attribs}->{bytes} + && $content_len < $session->{attribs}->{bytes} ) { + $session->{data_len} = $session->{attribs}->{bytes}; + $session->{buff} = $content; + $session->{buff_left} = $session->{attribs}->{bytes} - $content_len; + MKDEBUG && _d('Contents not complete,', $session->{buff_left}, + 'bytes left'); + $session->{state} = 'recving content'; + return; + } + } + elsif ( $session->{state} eq 'recving content' ) { + if ( $session->{buff} ) { + MKDEBUG && _d('Receiving content,', $session->{buff_left}, + 'bytes left'); + return; + } + MKDEBUG && _d('Contents received'); + } + else { + warn "Server response in unknown state"; + return; + } + + MKDEBUG && _d('Creating event, deleting session'); + $session->{end_reply} = $session->{ts_max} || $packet->{ts}; + my $event = $self->make_event($session, $packet); + delete $self->{sessions}->{$session->{client}}; # http is stateless! + return $event; +} + +sub _packet_from_client { + my ( $self, $packet, $session, $misc ) = @_; + die "I need a packet" unless $packet; + die "I need a session" unless $session; + + MKDEBUG && _d('Packet is from client; state:', $session->{state}); + + my $event; + if ( ($session->{state} || '') =~ m/awaiting / ) { + MKDEBUG && _d('More client headers:', $packet->{data}); + return; + } + + if ( !$session->{state} ) { + $session->{state} = 'awaiting reply'; + my ($line1, undef) = $self->_parse_header($session, $packet->{data}, $packet->{data_len}); + my ($request, $page, $version) = $line1 =~ m/(\S+)/g; + if ( !$request || !$page ) { + MKDEBUG && _d("Didn't get a request or page:", $request, $page); + return; + } + $request = lc $request; + my $vh = $session->{attribs}->{Virtual_host} || ''; + my $arg = "$request $vh$page"; + MKDEBUG && _d('arg:', $arg); + + if ( $request eq 'get' || $request eq 'post' ) { + @{$session->{attribs}}{qw(arg)} = ($arg); + } + else { + MKDEBUG && _d("Don't know how to handle a", $request, "request"); + return; + } + + $session->{start_request} = $packet->{ts}; + $session->{attribs}->{host} = $packet->{src_host}; + $session->{attribs}->{pos_in_log} = $packet->{pos_in_log}; + $session->{attribs}->{ts} = $packet->{ts}; + } + else { + die "Probably multiple GETs from client before a server response?"; + } + + return $event; +} + +sub _parse_header { + my ( $self, $session, $data, $len, $no_recurse ) = @_; + die "I need data" unless $data; + my ($header, $content) = split(/\r\n\r\n/, $data); + my ($line1, $header_vals) = $header =~ m/\A(\S+ \S+ .+?)\r\n(.+)?/s; + MKDEBUG && _d('HTTP header:', $line1); + return unless $line1; + + if ( !$header_vals ) { + MKDEBUG && _d('No header vals'); + return $line1, undef; + } + my @headers; + foreach my $val ( split(/\r\n/, $header_vals) ) { + last unless $val; + MKDEBUG && _d('HTTP header:', $val); + if ( $val =~ m/^Content-Length/i ) { + ($session->{attribs}->{bytes}) = $val =~ /: (\d+)/; + MKDEBUG && _d('Saved Content-Length:', $session->{attribs}->{bytes}); + } + if ( $val =~ m/Content-Encoding/i ) { + ($session->{compressed}) = $val =~ /: (\w+)/; + MKDEBUG && _d('Saved Content-Encoding:', $session->{compressed}); + } + if ( $val =~ m/^Host/i ) { + ($session->{attribs}->{Virtual_host}) = $val =~ /: (\S+)/; + MKDEBUG && _d('Saved Host:', ($session->{attribs}->{Virtual_host})); + } + } + return $line1, $content; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End HTTPProtocolParser package +# ########################################################################### + +# ########################################################################### +# ExecutionThrottler package 5204 +# ########################################################################### +package ExecutionThrottler; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); + +use List::Util qw(sum min max); +use Time::HiRes qw(time); +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; +$Data::Dumper::Quotekeys = 0; + +use constant MKDEBUG => $ENV{MKDEBUG}; + +sub new { + my ( $class, %args ) = @_; + my @required_args = qw(rate_max get_rate check_int step); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless defined $args{$arg}; + } + my $self = { + step => 0.05, # default + %args, + rate_ok => undef, + last_check => undef, + stats => { + rate_avg => 0, + rate_samples => [], + }, + int_rates => [], + skip_prob => 0.0, + }; + + return bless $self, $class; +} + +sub throttle { + my ( $self, %args ) = @_; + my $time = $args{misc}->{time} || time; + if ( $self->_time_to_check($time) ) { + my $rate_avg = (sum(@{$self->{int_rates}}) || 0) + / (scalar @{$self->{int_rates}} || 1); + my $running_avg = $self->_save_rate_avg($rate_avg); + MKDEBUG && _d('Average rate for last interval:', $rate_avg); + + if ( $args{stats} ) { + $args{stats}->{throttle_checked_rate}++; + $args{stats}->{throttle_rate_avg} = sprintf '%.2f', $running_avg; + } + + @{$self->{int_rates}} = (); + + if ( $rate_avg > $self->{rate_max} ) { + $self->{skip_prob} += $self->{step}; + $self->{skip_prob} = 1.0 if $self->{skip_prob} > 1.0; + MKDEBUG && _d('Rate max exceeded'); + $args{stats}->{throttle_rate_max_exceeded}++ if $args{stats}; + } + else { + $self->{skip_prob} -= $self->{step}; + $self->{skip_prob} = 0.0 if $self->{skip_prob} < 0.0; + $args{stats}->{throttle_rate_ok}++ if $args{stats}; + } + + MKDEBUG && _d('Skip probability:', $self->{skip_prob}); + $self->{last_check} = $time; + } + else { + my $current_rate = $self->{get_rate}->(); + push @{$self->{int_rates}}, $current_rate; + if ( $args{stats} ) { + $args{stats}->{throttle_rate_min} = min( + ($args{stats}->{throttle_rate_min} || ()), $current_rate); + $args{stats}->{throttle_rate_max} = max( + ($args{stats}->{throttle_rate_max} || ()), $current_rate); + } + MKDEBUG && _d('Current rate:', $current_rate); + } + + if ( $args{event} ) { + $args{event}->{Skip_exec} = $self->{skip_prob} <= rand() ? 'No' : 'Yes'; + } + + return $args{event}; +} + +sub _time_to_check { + my ( $self, $time ) = @_; + if ( !$self->{last_check} ) { + $self->{last_check} = $time; + return 0; + } + return $time - $self->{last_check} >= $self->{check_int} ? 1 : 0; +} + +sub rate_avg { + my ( $self ) = @_; + return $self->{stats}->{rate_avg} || 0; +} + +sub skip_probability { + my ( $self ) = @_; + return $self->{skip_prob}; +} + +sub _save_rate_avg { + my ( $self, $rate ) = @_; + my $samples = $self->{stats}->{rate_samples}; + push @$samples, $rate; + shift @$samples if @$samples > 1_000; + $self->{stats}->{rate_avg} = sum(@$samples) / (scalar @$samples); + return $self->{stats}->{rate_avg} || 0; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; + +# ########################################################################### +# End ExecutionThrottler package +# ########################################################################### + +# ########################################################################### +# This is a combination of modules and programs in one -- a runnable module. +# http://www.perl.com/pub/a/2006/07/13/lightning-articles.html?page=last +# Or, look it up in the Camel book on pages 642 and 643 in the 3rd edition. +# +# Check at the end of this package for the call to main() which actually runs +# the program. +# ########################################################################### +package mk_query_digest; + +use English qw(-no_match_vars); +use Time::Local qw(timelocal); +use Time::HiRes qw(time usleep); +use Data::Dumper; +$Data::Dumper::Indent = 1; +$OUTPUT_AUTOFLUSH = 1; + +Transformers->import(qw(shorten micro_t percentage_of ts make_checksum + any_unix_timestamp)); + +use constant MKDEBUG => $ENV{MKDEBUG}; + +use sigtrap 'handler', \&sig_int, 'normal-signals'; + +# Global variables. Only really essential variables should be here. +my $oktorun = 1; +my $dp = new DSNParser ( + { key => 'D', copy => 1, desc => 'Database that contains the query review table' }, + { key => 't', desc => 'Table to use as the query review table' } ); +my $q = new Quoter(); +my $qp = new QueryParser(); +my $qr = new QueryRewriter(QueryParser=>$qp); +my $ex_dbh; # For --execute +my $ep_dbh; # For --explain +my $ps_dbh; # For Processlist +my $aux_dbh; # For --aux-dsn (--since/--until "MySQL expression") + +sub main { + @ARGV = @_; # set global ARGV for this package + + # ########################################################################## + # Get configuration information. + # ########################################################################## + my $o = new OptionParser( + strict => 0, + dp => $dp, + prompt => '[OPTION...] [FILE]', + description => q{parses and analyzes MySQL log files. With no } + . q{FILE, or when FILE is -, read standard input.}, + ); + $o->get_specs(); + $o->get_opts(); + + $dp->prop('set-vars', $o->get('set-vars')); + + # Frequently used options. + my $review_dsn = $o->get('review'); + my @groupby = @{$o->get('group-by')}; + my @orderby; + if ( (grep { $_ eq 'genlog' || $_ eq 'GeneralLogParser' } @{$o->get('type')}) + && !$o->got('order-by') ) { + @orderby = 'Query_time:cnt'; + } + else { + @orderby = @{$o->get('order-by')}; + } + + if ( !$o->get('help') ) { + if ( $review_dsn + && (!defined $review_dsn->{D} || !defined $review_dsn->{t}) ) { + $o->save_error('The --review DSN requires a D (database) and t' + . ' (table) part specifying the query review table'); + } + if ( $o->get('mirror') + && (!$o->get('execute') || !$o->get('processlist')) ) { + $o->save_error('--mirror requires --execute and --processlist'); + } + if ( $o->get('outliers') + && grep { $_ !~ m/^\w+:[0-9.]+(?::[0-9.]+)?$/ } @{$o->get('outliers')} + ) { + $o->save_error('--outliers requires two or three colon-separated fields'); + } + if ( $o->get('execute-throttle') ) { + my ($rate_max, $int, $step) = @{$o->get('execute-throttle')}; + $o->save_error("--execute-throttle max time must be between 1 and 100") + unless $rate_max && $rate_max > 0 && $rate_max <= 100; + $o->save_error("No check interval value for --execute-throttle") + unless $int; + $o->save_error("--execute-throttle check interval must be an integer") + if $int =~ m/[^\d]/; + $o->save_error("--execute-throttle step must be between 1 and 100") + if $step && ($step < 1 || $step > 100); + } + } + + # Set an orderby for each groupby; use the default orderby if there + # are more groupby than orderby attribs. + my $default_orderby = $o->get_defaults()->{'order-by'}; + foreach my $i ( 0..$#groupby ) { + $orderby[$i] ||= $default_orderby; + } + $o->set('order-by', \@orderby); + + $o->usage_or_errors(); + + # ######################################################################## + # Set up for --explain + # ######################################################################## + if ( $o->get('explain') ) { + $ep_dbh = $dp->get_dbh( + $dp->get_cxn_params($o->get('explain')), {AutoCommit => 1}); + $ep_dbh->{InactiveDestroy} = 1; # Don't die on fork(). + } + + # ######################################################################## + # Set up for --review and --review-history. + # ######################################################################## + my $qv; # QueryReview + my $qv_dbh; # For QueryReview + my $qv_dbh2; # For QueryReview and --review-history + if ( $review_dsn ) { + my $tp = new TableParser(Quoter => $q); + my $du = new MySQLDump(); + $qv_dbh = $dp->get_dbh( + $dp->get_cxn_params($review_dsn), {AutoCommit => 1}); + $qv_dbh->{InactiveDestroy} = 1; # Don't die on fork(). + my @db_tbl = @{$review_dsn}{qw(D t)}; + my $db_tbl = $q->quote(@db_tbl); + + # Create the review table if desired + if ( $o->get('create-review-table') ) { + my $sql = $o->read_para_after( + __FILE__, qr/MAGIC_create_review/); + $sql =~ s/query_review/IF NOT EXISTS $db_tbl/; + MKDEBUG && _d($sql); + $qv_dbh->do($sql); + } + + # Check for existence and the permissions to insert into the + # table. + if ( !$tp->check_table( + dbh => $qv_dbh, + db => $db_tbl[0], + tbl => $db_tbl[1], + all_privs => 1) ) + { + die "The query review table $db_tbl " + . "does not exist or you do not have INSERT privileges"; + } + + # Set up the new QueryReview object. + my $struct = $tp->parse($du->get_create_table($qv_dbh, $q, @db_tbl)); + $qv = new QueryReview( + dbh => $qv_dbh, + db_tbl => $db_tbl, + tbl_struct => $struct, + quoter => $q, + ); + + # Set up the review-history table + if ( $o->get('review-history') ) { + $qv_dbh2 = $dp->get_dbh( + $dp->get_cxn_params($o->get('review-history')), {AutoCommit => 1}); + $qv_dbh2->{InactiveDestroy} = 1; # Don't die on fork(). + my @hdb_tbl = @{$o->get('review-history')}{qw(D t)}; + my $hdb_tbl = $q->quote(@hdb_tbl); + + # Create the review-history table if desired + if ( $o->get('create-review-history-table') ) { + my $sql = $o->read_para_after( + __FILE__, qr/MAGIC_create_review_history/); + $sql =~ s/query_review_history/IF NOT EXISTS $hdb_tbl/; + MKDEBUG && _d($sql); + $qv_dbh2->do($sql); + } + + # Check for existence and the permissions to insert into the + # table. + if ( !$tp->check_table( + dbh => $qv_dbh2, + db => $hdb_tbl[0], + tbl => $hdb_tbl[1], + all_privs => 1) ) + { + die "The query review history table $hdb_tbl " + . "does not exist or you do not have INSERT privileges"; + } + + # Inspect for MAGIC_history_cols. Add them to the --select list + # only if an explicit --select list was given. Otherwise, leave + # --select undef which will cause EventAggregator to aggregate every + # attribute available which will include the history columns. + # If no --select list was given and we make one by adding the history + # columsn to it, then EventAggregator will only aggregate the + # history columns and nothing else--we don't want this. + my $tbl = $tp->parse($du->get_create_table($qv_dbh2, $q, @hdb_tbl)); + my $pat = $o->read_para_after(__FILE__, qr/MAGIC_history_cols/); + $pat =~ s/\s+//g; + $pat = qr/^(.*?)_($pat)$/; + # Get original --select values. + my %select = map { $_ => 1 } @{$o->get('select')}; + foreach my $col ( @{$tbl->{cols}} ) { + my ( $attr, $metric ) = $col =~ m/$pat/; + next unless $attr && $metric; + $attr = ucfirst $attr if $attr =~ m/_/; # TableParser lowercases + # Add history table values to original select values. + $select{$attr}++; + } + + if ( $o->got('select') ) { + # Re-set --select with its original values plus the history + # table values. + $o->set('select', [keys %select]); + MKDEBUG && _d("--select after parsing --review-history table:", + @{$o->get('select')}); + } + + # And tell the QueryReview that it has more work to do. + $qv->set_history_options( + table => $hdb_tbl, + dbh => $qv_dbh2, + tbl_struct => $tbl, + col_pat => $pat, + ); + } + } + + # ######################################################################## + # Set up an array of callbacks to filter and transform events. The first + # one should add the fingerprint to the event (except if we're parsing + # tcpdump output; see below). After that, callbacks can do anything, as + # long as they return the event (failing to return the event terminates + # the chain). + # ######################################################################## + my @callbacks; + my %callback_names; + my %prof; # pipeline profile + my %stats; # general stats + my $misc = {}; + if ( $o->get('processlist') ) { + my $pl = new Processlist(); + my ( $sth, $cxn ); + my $cur_server = 'processlist'; + my $cur_time = 0; + + $misc = { + time => 0, + etime => 0, + code => sub { + my $time = $misc->{time} = time(); + my $err; + do { + eval { $sth->execute; }; + $err = $EVAL_ERROR; + if ( $err ) { # Try to reconnect when there's an error. + @{$misc}{qw(time etime)} = (0, 0); + eval { + ($cur_server, $ps_dbh) = find_role( + $o, $ps_dbh, $cur_server, 0, 'for --processlist'); + $cur_time = time(); + $sth = $ps_dbh->prepare('SHOW FULL PROCESSLIST'); + $cxn = $ps_dbh->{mysql_thread_id}; + $sth->execute(); + }; + $err = $EVAL_ERROR; + if ( $err ) { + print STDERR $err; + sleep 1; + } + } + } until ( $sth && !$err ); + if ( $o->get('mirror') + && time() - $cur_time > $o->get('mirror')) { + ($cur_server, $ps_dbh) = find_role($o, $ps_dbh, $cur_server, + 0, 'for --processlist'); + $cur_time = time(); + } + $misc->{etime} = time() - $time; + [ grep { $_->[0] != $cxn } @{ $sth->fetchall_arrayref(); } ]; + }, + }; + + push @callbacks, sub { + my ( $event, %args ) = @_; + return $pl->parse_event(%args); + }; + $callback_names{$callbacks[-1]} = ref $pl; + MKDEBUG && _d('Added Processlist module to callbacks'); + } + else { + my %alias_for = ( + slowlog => ['SlowLogParser'], + binlog => ['BinaryLogParser'], + genlog => ['GeneralLogParser'], + tcpdump => ['TcpdumpParser','MySQLProtocolParser'], + memcached => ['TcpdumpParser','MemcachedProtocolParser', + 'MemcachedEvent'], + http => ['TcpdumpParser','HTTPProtocolParser'], + ); + my $type = $o->get('type'); + $type = $alias_for{$type->[0]} if $alias_for{$type->[0]}; + + foreach my $module ( @$type ) { + my $parser; + eval { + $parser = $module->new( + server => $o->get('watch-server'), + o => $o, + ); + }; + if ( $EVAL_ERROR ) { + die "Failed to load $module module: $EVAL_ERROR"; + } + push @callbacks, sub { + my ( $event, %args ) = @_; + return $parser->parse_event(%args); + }; + $callback_names{$callbacks[-1]} = ref $parser; + MKDEBUG && _d('Added', $module, 'module to callbacks'); + } + } + + # Filter early for --since and --until. + my $past_since; + my $at_until; + # If --since or --until is a MySQL expression, then any_unix_timestamp() + # will need this callback to execute the expression. We don't know what + # type of time value the user gave, so we'll create the callback in any case. + if ( my $aux_dsn = $o->get('aux-dsn') ) { + $aux_dbh = $dp->get_dbh($dp->get_cxn_params($aux_dsn), {AutoCommit => 1}); + $aux_dbh->{InactiveDestroy} = 1; # Don't die on fork(). + } + $aux_dbh ||= $qv_dbh || $qv_dbh2 || $ex_dbh || $ps_dbh || $ep_dbh; + MKDEBUG && _d('aux dbh:', $aux_dbh); + my $time_callback = sub { + my ( $exp ) = @_; + return unless $aux_dbh; + my $sql = "SELECT UNIX_TIMESTAMP($exp)"; + MKDEBUG && _d($sql); + return $aux_dbh->selectall_arrayref($sql)->[0]->[0]; + }; + if ( $o->get('since') ) { + my $since = any_unix_timestamp($o->get('since'), $time_callback); + die "Invalid --since value" unless $since; + push @callbacks, sub { + my ( $event ) = @_; + MKDEBUG && _d('callback: --since'); + if ( $past_since ) { + MKDEBUG && _d('Already past --since'); + return $event; + } + if ( $event->{ts} ) { + my $ts = any_unix_timestamp($event->{ts}, $time_callback); + if ( ($ts || 0) >= $since ) { + MKDEBUG && _d('Event is at or past --since'); + $past_since = 1; + return $event; + } + else { + MKDEBUG && _d('Event is before --since'); + } + } + return; + }; + $callback_names{$callbacks[-1]} = 'since'; + } + if ( $o->get('until') ) { + my $until = any_unix_timestamp($o->get('until'), $time_callback); + die "Invalid --until value" unless $until; + push @callbacks, sub { + my ( $event ) = @_; + MKDEBUG && _d('callback: --until'); + if ( $at_until ) { + MKDEBUG && _d('Already past --until'); + return; + } + if ( $event->{ts} ) { + my $ts = any_unix_timestamp($event->{ts}, $time_callback); + if ( ($ts || 0) >= $until ) { + MKDEBUG && _d('Event at or after --until'); + $at_until = 1; + return; + } + else { + MKDEBUG && _d('Event is before --until'); + } + } + return $event; + }; + $callback_names{$callbacks[-1]} = 'until'; + } + + if ( grep { $_ eq 'fingerprint' } @groupby ) { + push @callbacks, sub { + my ( $event ) = @_; + MKDEBUG && _d('callback: fingerprint'); + # Skip events which do not have the groupby attribute. + my $groupby_val = $event->{arg}; + return unless $groupby_val; + $event->{fingerprint} = $qr->fingerprint($groupby_val); + return $event; + }; + $callback_names{$callbacks[-1]} = 'fingerprint'; + } + + # Make subs which map attrib aliases to their primary attrib. + foreach my $alt_attrib ( @{$o->get('attribute-aliases')} ) { + push @callbacks, make_alt_attrib($alt_attrib); + $callback_names{$callbacks[-1]} = 'attribute aliases'; + } + + # Carry attribs forward for --inherit-attributes. + my $prev_vals = {}; + my $inherit_attrib_sub = make_inherit_attribs( + $o->get('inherit-attributes'), + $prev_vals + ); + push @callbacks, $inherit_attrib_sub if $inherit_attrib_sub; + $callback_names{$callbacks[-1]} = 'inherit attributes'; + + if ( grep { $_ eq 'tables' } @groupby ) { + push @callbacks, sub { + my ( $event ) = @_; + MKDEBUG && _d('callback: tables'); + my $group_by_val = $event->{arg}; + return 0 unless defined $group_by_val; + $event->{tables} = [ + map { + # Canonicalize and add the db name in front + $_ =~ s/`//g; + if ($_ !~ m/\./ && (my $db = $event->{db} || $event->{Schema})) { + $_ = "$db.$_"; + } + $_; + } + $qp->get_tables($group_by_val) + ]; + return $event; + }; + $callback_names{$callbacks[-1]} = 'tables'; + } + + my %distill_args; + if ( $o->get('type') eq 'memcached' || $o->get('type') eq 'http' ) { + $distill_args{generic} = 1; + if ( $o->get('type') eq 'http' ) { + # Remove stuff after url. + $distill_args{trf} = sub { + my ( $query ) = @_; + $query =~ s/(\S+ \S+?)(?:[?;].+)/$1/; + return $query; + }; + } + } + if ( grep { $_ eq 'distill' } @groupby ) { + push @callbacks, sub { + my ( $event ) = @_; + MKDEBUG && _d('callback: distill'); + my $group_by_val = $event->{arg}; + return 0 unless defined $group_by_val; + $event->{distill} = $qr->distill($group_by_val, %distill_args); + MKDEBUG && !$event->{distill} && _d('Cannot distill', $event->{arg}); + return $event; + }; + $callback_names{$callbacks[-1]} = 'distill'; + } + + # Filter after special attributes, like fingerprint, tables, + # distill, etc., have been created. + if ( $o->get('filter') ) { + my $filter = $o->get('filter'); + if ( -f $filter && -r $filter ) { + MKDEBUG && _d('Reading file', $filter, 'for --filter code'); + open my $fh, "<", $filter or die "Cannot open $filter: $OS_ERROR"; + $filter = do { local $/ = undef; <$fh> }; + close $fh; + } + else { + $filter = "( $filter )"; # issue 565 + } + my $code = "sub { MKDEBUG && _d('callback: filter'); my(\$event) = shift; $filter && return \$event; };"; + MKDEBUG && _d('--filter code:', $code); + my $sub = eval $code + or die "Error compiling --filter code: $code\n$EVAL_ERROR"; + push @callbacks, $sub; + $callback_names{$callbacks[-1]} = 'filter'; + } + + if ( $o->get('zero-admin') ) { + push @callbacks, sub { + my ( $event ) = @_; + MKDEBUG && _d('callback: zero admin'); + if ( $event->{arg} && $event->{arg} =~ m/^# administrator/ ) { + $event->{Rows_sent} = 0; + $event->{Rows_read} = 0; + $event->{Rows_examined} = 0; + } + return $event; + }; + $callback_names{$callbacks[-1]} = 'zero admin'; + } + + if ( $o->got('sample') ) { + my $group_by_val = $groupby[0]; + my $num_samples = $o->get('sample'); + if ( $group_by_val ) { + my %seen; + push @callbacks, sub { + my ($event) = @_; + MKDEBUG && _d('callback: sample'); + if ( ++$seen{$event->{$group_by_val}} <= $num_samples ) { + MKDEBUG && _d("--sample permits event", + $event->{$group_by_val}); + return $event; + } + MKDEBUG && _d("--sample rejects event", $event->{$group_by_val}); + return; + }; + $callback_names{$callbacks[-1]} = 'sample'; + } + } + + if ( $o->get('print') ) { + my $w = new SlowLogWriter(); + push @callbacks, sub { + my ( $event ) = @_; + MKDEBUG && _d('callback: print'); + $w->write(*STDOUT, $event); + return $event; + }; + $callback_names{$callbacks[-1]} = 'print'; + } + + my $et; + if ( my $et_args = $o->get('execute-throttle') ) { + # These were check earlier; no need to check them again. + my ($rate_max, $int, $step) = @{$o->get('execute-throttle')}; + $step ||= 5; + $step /= 100; # step specified as percent but $et expect 0.1=10%, etc. + MKDEBUG && _d('Execute throttle:', $rate_max, $int, $step); + + my $get_rate = sub { + return percentage_of( + $prof{callback}->{'execute'}->{time} || 0, + $prof{total}->{time} || 0, + ); + }; + + $et = new ExecutionThrottler( + rate_max => $rate_max, + get_rate => $get_rate, + check_int => $int, + step => $step, + ); + push @callbacks, sub { + my ( $event, %args ) = @_; + MKDEBUG && _d('callback: throttle'); + return $et->throttle(%args); + }; + $callback_names{$callbacks[-1]} = 'execute throttle'; + } + + if ( $o->get('execute') ) { + my $cur_server = 'execute'; + ($cur_server, $ex_dbh) = find_role($o, $ex_dbh, $cur_server, + 1, 'for --execute'); + my $cur_time = time(); + my $curdb; + my $default_db = $o->get('execute')->{D}; + MKDEBUG && _d('Default db:', $default_db); + push @callbacks, sub { + my ( $event ) = @_; + MKDEBUG && _d('callback: execute'); + $event->{Exec_orig_time} = $event->{Query_time}; + if ( ($event->{Skip_exec} || '') eq 'Yes' ) { + MKDEBUG && _d('Not executing event because of --execute-throttle'); + # Zero Query_time to 'Exec time' will show the real time + # spent executing queries. + $event->{Query_time} = 0; + $stats{execute_skipped}++; + return $event; + } + $stats{execute_executed}++; + my $db = $event->{db} || $default_db; + eval { + if ( $db && (!$curdb || $db ne $curdb) ) { + MKDEBUG && _d('USE ', $db); + $ex_dbh->do("USE $db"); + $curdb = $db; + } + my $start = time(); + $ex_dbh->do($event->{arg}); + my $end = time(); + $event->{Query_time} = $end - $start; + $event->{Exec_diff_time} + = $event->{Query_time} - $event->{Exec_orig_time}; + if ( $o->get('mirror') && $end - $cur_time > $o->get('mirror') ) { + ($cur_server, $ex_dbh) = find_role($o, $ex_dbh, $cur_server, + 1, 'for --execute'); + $cur_time = $end; + } + }; + if ( $EVAL_ERROR ) { + MKDEBUG && _d($EVAL_ERROR); + $stats{execute_error}++; + # Don't try to re-execute the statement. Just skip it. + if ( $EVAL_ERROR =~ m/server has gone away/ ) { + print STDERR $EVAL_ERROR; + eval { + ($cur_server, $ex_dbh) = find_role($o, $ex_dbh, $cur_server, + 1, 'for --execute'); + $cur_time = time(); + }; + if ( $EVAL_ERROR ) { + print STDERR $EVAL_ERROR; + sleep 1; + } + return 0; + } + if ( $EVAL_ERROR =~ m/No database/ ) { + $stats{execute_no_database}++; + } + } + return $event; + }; + $callback_names{$callbacks[-1]} = 'execute'; + } + + # Finally, add aggregator obj for each groupby attrib to the callbacks. + # These aggregating objs should be the last callbacks. + my @ea; # EventAggregators + my @tl; # EventTimeline (aggregators) + foreach my $i ( 0..$#groupby ) { + my $groupby = $groupby[$i]; + + # This shouldn't happen. + die "No --order-by value for --group-by $groupby" unless $orderby[$i]; + + my ( $orderby_attrib, $orderby_func ) = split(/:/, $orderby[$i]); + + my %attributes = map { + my ($name, @alt) = split(/:/, $_); + $name => [$name, @alt]; + } + grep { $_ !~ m/^$groupby\b/ } + @{$o->get('select')}; + + # Create an EventAggregator for this groupby attrib and + # add it to callbacks. + my $ea = new EventAggregator( + groupby => $groupby, + attributes => { %attributes }, + worst => $orderby_attrib, + attrib_limit => $o->get('attribute-value-limit'), + ignore_attributes => $o->get('ignore-attributes'), + unroll_limit => $o->get('check-attributes-limit'), + type_for => { + val => 'string', + key_print => 'string', + Status_code => 'string', + }, + ); + push @ea, $ea; + push @callbacks, sub { + my ( $event ) = @_; + $ea->aggregate($event); + return $event; + }; + $callback_names{$callbacks[-1]} = "aggregate $groupby"; + + # If user wants a timeline report, too, then create an EventTimeline + # aggregator for this groupby attrib and add it to the callbacks, too. + if ( $o->get('timeline') ) { + my $tl = new EventTimeline( + groupby => [$groupby], + attributes => [qw(Query_time ts)], + ); + push @tl, $tl; + push @callbacks, sub { + my ( $event ) = @_; + $tl->aggregate($event); + return $event; + }; + $callback_names{$callbacks[-1]} = "timeline $groupby"; + } + } + + if ( $o->get('processlist') ) { + push @callbacks, sub { + MKDEBUG && _d('callback: interval sleep'); + usleep($o->get('interval') * 1_000_000); + return @_; + }; + $callback_names{$callbacks[-1]} = 'interval sleep'; + } + + # ######################################################################## + # Daemonize now that everything is setup and ready to work. + # ######################################################################## + my $daemon; + if ( $o->get('daemonize') ) { + $daemon = new Daemon(o=>$o); + $daemon->daemonize(); + MKDEBUG && _d('I am a daemon now'); + } + elsif ( $o->get('pid') ) { + # We're not daemoninzing, it just handles PID stuff. + $daemon = new Daemon(o=>$o); + $daemon->make_PID_file(); + } + + # ########################################################################## + # Parse the input. + # ########################################################################## + if ( my $patterns = $o->get('embedded-attributes') ) { + $misc->{embed} = qr/$patterns->[0]/o; + $misc->{capture} = qr/$patterns->[1]/o; + MKDEBUG && _d('Patterns for embedded attributes:', $misc->{embed}, + $misc->{capture}); + } + + if ( @ARGV == 0 ) { + push @ARGV, '-'; # Magical STDIN filename. + } + + my $fh; + my $start = time(); + my $end = $start + ($o->get('run-time') || 0); # When we should exit + my $now = $start; + my $iters = 0; + ITERATION: + while ( # Quit if instructed to, or if iterations are exceeded. + $oktorun + && (!$o->get('iterations') || $iters++ < $o->get('iterations') ) + ) { + + if ( $o->get('print-iterations') ) { + my $iter_start = time; + MKDEBUG && _d('Iteration', $iters, 'started at', ts($iter_start)); + print "\n# Iteration $iters started at ", ts($iter_start), "\n"; + } + + EVENT: + while ( # Quit if: + $oktorun # instructed to quit + && ($start == $end || $now < $end) ) # or time is exceeded + { + if ( !$fh ) { + my $file = shift @ARGV; + if ( !$file ) { + MKDEBUG && _d('No more files to parse'); + last EVENT; + } + + if ( $file eq '-' ) { + $fh = *STDIN; + MKDEBUG && _d('Reading STDIN'); + } + else { + if ( !open $fh, "<", $file ) { + $fh = undef; + warn "Cannot open $file: $OS_ERROR\n"; + next EVENT; + } + MKDEBUG && _d('Reading', $file); + + # Reset these var in case we read two logs out of order by time. + $past_since = 0; + $at_until = 0; + } + } + + eval { + # Run events through the pipeline. The first pipeline process + # is usually responsible for getting the next event. The pipeline + # stops if a process does not return the event. This main loop + # stops if a process sets oktorun to false; it usually does this + # when there are no more events, but it may do it for other reasons. + # Some inputs are infinite streams of events, like the proclist, + # so oktorun is never set to false and it's the user's job to tell + # us when to stop. + my $event = {}; + my $more_events = 1; + my $oktorun_sub = sub { $more_events = $_[0]; }; + foreach my $callback ( @callbacks ) { + last unless $oktorun; # the global oktorun var + my $start = time; + $event = $callback->( + $event, # for backwards compatibility + event => $event, # new interface + fh => $fh, + oktorun => $oktorun_sub, + misc => $misc, + stats => \%stats, + ); + my $end = time; + my $t = $end - $start; + my $name = $callback_names{$callback} || $callback; + $prof{callback}->{$name}->{time} += $t; + $prof{callback}->{$name}->{count}++; + $prof{total}->{time} += $t; + $prof{total}->{count}++; + last unless $event; + } + if ( !$more_events ) { + MKDEBUG && _d('No more events'); + close $fh if $fh; + $fh = undef; + } + }; + if ( $EVAL_ERROR ) { + _d($EVAL_ERROR); + $stats{pipeline_error}++; + + # Don't ignore failure to open a file, else we'll get + # "tell() on closed filehandle" errors. + last EVENT if $EVAL_ERROR =~ m/Cannot open/; + + last EVENT unless $o->get('continue-on-error'); + } + $now = time(); + } # EVENT + + # ###################################################################### + # Done parsing events, now do something with the aggregated query + # results for each class (i.e. each groupby), probably --report them, + # or maybe just save them for --review. + # ###################################################################### + my $qrf = new QueryReportFormatter(); + my @select = ( $ea[0]->get_attributes() ); + MKDEBUG && _d('attribs for reports:', @select); + + # First, and only once, print the global report analysis header. + if ( $o->get('report') && $o->get('report-format')->{header} ) { + print $qrf->header() if $o->get('report-format')->{rusage}; + + # It doesn't matter which ea we use here because they will all have + # the same attributes; they only differ in how they were grouped. + my $ea = $ea[0]; + if ( !$ea->events_processed() ) { + print "# No events processed.\n"; + } + else { + my ( $orderby_attrib, $orderby_func ) = split(/:/, $orderby[0]); + $orderby_attrib = check_orderby_attrib($orderby_attrib, $ea, $o); + print $qrf->global_report( + $ea, + select => [ grep { $_ !~ m/^(?:user|db|pos_in_log)$/ } @select ], + worst => $orderby_attrib, + no_zero_bool => !$o->get('zero-bool'), + ); + } + } + + # ##################################################################### + # Do the reports for each groupby/class. + # ##################################################################### + for my $i ( 0..$#groupby ) { + my $groupby = $groupby[$i]; + my $ea = $ea[$i]; + my ( $orderby_attrib, $orderby_func ) = split(/:/, $orderby[$i]); + $orderby_attrib = check_orderby_attrib($orderby_attrib, $ea, $o); + MKDEBUG && _d('Doing reports for groupby', $groupby, 'orderby', + $orderby_attrib, $orderby_func); + + # We don't report on all queries, just the worst, i.e. the top + # however many. + my $limit = $o->get('limit')->[$i] || '95%:20'; + my ($total, $count); + if ( $limit =~ m/^\d+$/ ) { + $count = $limit; + } + else { + # It's a percentage, so grab as many as needed to get to + # that % of the file. + ($total, $count) = $limit =~ m/(\d+)/g; + $total *= ($ea->results->{globals}->{$orderby_attrib}->{sum} || 0) + / 100; + } + my %top_spec = ( + attrib => $orderby_attrib, + orderby => $orderby_func || 'cnt', + total => $total, + count => $count, + ); + if ( $o->get('outliers')->[$i] ) { + @top_spec{qw(ol_attrib ol_limit ol_freq)} + = split(/:/, $o->get('outliers')->[$i]); + } + # The queries that will be reported. + my @worst = $ea->top_events(%top_spec); + + my $expected_range = $o->get('expected-range'); + my $explain_why + = $expected_range + && (@worst < $expected_range->[0] || @worst > $expected_range->[1]); + + my @table_access; + my @profiles; + my $total_r = 0; + + # Print a header for this groupby/class if we're doing the standard + # query report and there's more than one class or there's one class + # but it's not the normal class grouped by fingerprint. + if ( $o->get('report') + && $o->get('report-format')->{query_report} + && (@groupby > 1 || $groupby ne 'fingerprint') ) + { + print "\n# ", ( '#' x 72 ), "\n"; + print "# Report grouped by $groupby\n"; + print '# ', ( '#' x 72 ), "\n"; + } + + # ################################################################## + # Do the reports for each query in this groupby/class. + # ################################################################## + ITEM: + foreach my $rank ( 1 .. @worst ) { + my $item = $worst[$rank - 1]->[0]; + my $stats = $ea->results->{classes}->{$item}; + my $sample = $ea->results->{samples}->{$item}; + my $samp_query = $sample->{arg} || ''; + + # ############################################################### + # Save the profile for this item. It's printed later, maybe. + # ############################################################### + my %profile = ( + rank => $rank, + r => $stats->{Query_time}->{sum}, + cnt => $stats->{Query_time}->{cnt}, + sample => $groupby eq 'fingerprint' ? $qr->distill($samp_query, + %distill_args) + : $item, + id => $groupby eq 'fingerprint' ? make_checksum($item) + : '', + ); + $total_r += $profile{r}; + push @profiles, \%profile; + + # ############################################################### + # Prepare for --review and --review-history. + # ############################################################### + my $review_vals; + my %history; + if ( $qv ) { + $review_vals = $qv->get_review_info($item); + if ( $review_vals->{reviewed_by} && !$o->get('report-all') ) { + # This item has already been reviewed, skip it. But before + # doing so, save the history if necessary. + if ( $o->get('review-history') ) { + $qv->set_review_history( + $item, $sample->{arg} || '', %history); + } + next ITEM; + } + + if ( $o->get('review-history') ) { + foreach my $attrib ( @select ) { + $history{$attrib} = $ea->metrics( + attrib => $attrib, + where => $item, + ); + } + } + } + + # ############################################################### + # Get tables for either --for-explain or --table-access. + # ############################################################### + my ($default_db) = $sample->{db} ? $sample->{db} + : $stats->{db}->{unq} ? keys %{$stats->{db}->{unq}} + : undef; + my @tables; + if ( $o->get('for-explain') || $o->get('table-access') ){ + @tables = extract_tables($samp_query, $default_db); + push @table_access, $samp_query if $o->get('table-access'); + } + + # ############################################################### + # Print the standard query analysis report. + # ############################################################### + if ( $o->get('report') + && $o->get('report-format')->{query_report} ) { + print "\n"; + print $qrf->event_report( + $ea, + select => [ grep { $_ !~ m/^(?:pos_in_log)$/ } @select ], + where => $item, + rank => $rank, + worst => $orderby_attrib, + reason => $explain_why ? $worst[$rank - 1]->[1] : '', + no_zero_bool => !$o->get('zero-bool'), + ); + print $qrf->chart_distro( + $ea, + attribute => $orderby_attrib, + where => $item, + ); + + if ( $qv ) { + # Print the review information that is already in the table + # before putting anything new into the table. + print "# Review information\n"; + foreach my $col ( $qv->review_cols() ) { + my $val = $review_vals->{$col}; + if ( !$val || $val ne '0000-00-00 00:00:00' ) { # issue 202 + printf "# %13s: %-s\n", $col, ($val ? $val : ''); + } + } + } + + if ( $groupby eq 'fingerprint' ) { + # Shorten it if necessary (issue 216 and 292). + $samp_query = $qr->shorten($samp_query, $o->get('shorten')) + if $o->get('shorten'); + } + # Print the query fingerprint. + if ( $groupby eq 'fingerprint' && $o->get('fingerprints') ) { + print "# Fingerprint\n# $item\n"; + } + + if ( $groupby eq 'fingerprint' ) { + print_tables(@tables) if $o->get('for-explain'); + if ( $item =~ m/^(?:[\(\s]*select|insert|replace)/ ) { + if ( $item =~ m/^(?:insert|replace)/ ) { # No EXPLAIN + print $samp_query, "\\G\n"; + } + else { + print "# EXPLAIN\n$samp_query\\G\n"; + print_explain($ep_dbh, $samp_query, $default_db); + } + } + else { + print "$samp_query\\G\n"; + my $converted = $qr->convert_to_select($samp_query); + if ( $o->get('for-explain') + && $converted + && $converted =~ m/^[\(\s]*select/i ) { + # It converted OK to a SELECT + print "# Converted for EXPLAIN\n# EXPLAIN\n"; + print "$converted\\G\n"; + } + } + } + else { + if ( $groupby eq 'tables' ) { + my ( $db, $tbl ) = $q->split_unquote($item); + print_tables([$db, $tbl]); + } + print $item, "\n"; + } + } # Print standard query analysis report + + # ############################################################### + # Update --review and --review-history. + # ############################################################### + if ( $qv ) { + $qv->set_review_info( + fingerprint => $item, + sample => $sample->{arg} || '', + first_seen => $stats->{ts}->{min}, + last_seen => $stats->{ts}->{max} + ); + if ( $o->get('review-history') ) { + $qv->set_review_history( + $item, $sample->{arg} || '', %history); + } + } + } # Each worst ITEM + + # ################################################################## + # Print per-class reports. + # ################################################################## + + # Print timeline. + if ( $o->get('timeline') ) { + my $tl = $tl[$i]; + $tl->report($tl->results(), sub { print @_ }); + } + + # Print table access. + if ( $o->get('table-access') ) { + my @subqueries; + map { + eval { + push @subqueries, $qp->split($_); + }; + if ( $EVAL_ERROR ) { + MKDEBUG && _d($EVAL_ERROR); + warn "Cannot get table access for query $_"; + } + } @table_access; + + my %seen; + print "\n" if $o->get('report'); + foreach my $subquery ( @subqueries ) { + my $rw = $qp->query_type($subquery, $qr)->{rw}; + next unless $rw; + my @tables = $qp->get_tables($subquery); + next unless scalar @tables; + foreach my $db_tbl ( @tables ) { + next if $seen{$db_tbl}++; # Unique-ify for issue 337. + my ($db, $tbl) = $q->split_unquote($db_tbl); + $db = $db ? "`$db`." : ''; + print "$rw $db`$tbl`\n"; + } + } + } + + # Print profile (issue 381). + if ( $o->get('report') && $o->get('report-format')->{profile} ) { + my $report = new ReportFormatter( + line_width => 74, + long_last_column => 1, + ); + $report->set_columns( + { name => 'Rank', right_justify => 1, }, + { name => 'Query ID', }, + { name => 'Response time', right_justify => 1, }, + { name => 'Calls', right_justify => 1, }, + { name => 'R/Call', right_justify => 1, }, + { name => 'Item', }, + ); + + foreach my $item ( sort { $a->{rank} <=> $b->{rank} } @profiles ) { + my $rt = sprintf('%10.4f', $item->{r}); + my $rtp = sprintf('%4.1f%%', $item->{r} / ($total_r || 1) * 100); + my $rc = sprintf('%8.4f', $item->{r} / $item->{cnt}); + $report->add_line( + $item->{rank}, + "0x$item->{id}", + "$rt $rtp", + $item->{cnt}, + $rc, + $item->{sample}, + ); + } + print "\n" . $report->get_report(); + } + } # Each groupby + + + # Reset the start/end/now times so the next iteration will run for the + # same amount of time. + $start = time(); + $end = $start + ($o->get('run-time') || 0); # When we should exit + $now = $start; + + foreach my $ea ( @ea, @tl ) { + $ea->reset_aggregated_data(); + } + + if ( $o->get('pipeline-profile') ) { + my $report = new ReportFormatter( + line_width => 74, + ); + $report->set_columns( + { name => 'Process' }, + { name => 'Time', right_justify => 1 }, + { name => 'Count', right_justify => 1 }, + ); + $report->set_title('Pipeline profile'); + foreach my $callback ( @callbacks ) { + my $name = $callback_names{$callback} || $callback; + my $t = $prof{callback}->{$name}->{time} || 0; + my $tp = sprintf('%.2f %4.1f%%', $t, + $t / ($prof{total}->{time} || 1) * 100); + $report->add_line($name, $tp, + $prof{callback}->{$name}->{count} || 0); + + # Reset profile for next iteration. + map { $prof{callback}->{$name}->{$_} = 0 } + keys %{$prof{callback}->{$name}}; + } + map { $prof{total}->{$_} = 0 } keys %{$prof{total}}; + + print "\n" . $report->get_report(); + } + + if ( $o->get('statistics') ) { + if ( keys %stats ) { + my $report = new ReportFormatter( + line_width => 74, + ); + $report->set_columns( + { name => 'Statistic', }, + { name => 'Value', right_justify => 1 }, + ); + foreach my $stat ( sort keys %stats ) { + $report->add_line($stat, $stats{$stat} || 0); + + # Reset stats for next iteration. + $stats{$stat} = 0; + } + print "\n" . $report->get_report(); + } + else { + print "\n# No statistics values.\n"; + } + } + } # ITERATION + + # Disconnect all open $dbh's + map { $dp->disconnect($_) } grep { $_ } + ($qv_dbh, $qv_dbh2, $ex_dbh, $ps_dbh, $ep_dbh); + + exit; +} # End main(). + +# ############################################################################ +# Subroutines. +# ############################################################################ +sub extract_tables { + my ( $query, $default_db ) = @_; + MKDEBUG && _d('Extracting tables'); + my @tables; + my %seen; + foreach my $db_tbl ( $qp->get_tables($query) ) { + next unless $db_tbl; + next if $seen{$db_tbl}++; # Unique-ify for issue 337. + my ( $db, $tbl ) = $q->split_unquote($db_tbl); + push @tables, [ $db || $default_db, $tbl ]; + } + return @tables; +} + +# Gets a default database and a list of arrayrefs of [db, tbl] to print out +sub print_tables { + my ( @tables ) = @_; + return unless @tables; + print "# Tables\n"; + foreach my $db_tbl ( @tables ) { + my ( $db, $tbl ) = @$db_tbl; + print '# SHOW TABLE STATUS', + ($db ? " FROM `$db`" : ''), " LIKE '$tbl'\\G\n"; + print "# SHOW CREATE TABLE ", + $q->quote(grep { $_ } @$db_tbl), "\\G\n"; + } + return; +} + +sub print_explain { + my ( $dbh, $query, $db ) = @_; + return unless $dbh && $query; + eval { + if ( !$qp->has_derived_table($query) ) { + if ( $db ) { + $dbh->do("USE " . $q->quote($db)); + } + my $sth = $dbh->prepare("EXPLAIN /*!50100 PARTITIONS */ $query"); + $sth->execute(); + my $i = 1; + while ( my @row = $sth->fetchrow_array() ) { + print "# *************************** ", $i++, + ". row ***************************\n"; + foreach my $j ( 0 .. $#row ) { + printf "# %13s: %s\n", $sth->{NAME}->[$j], + defined $row[$j] ? $row[$j] : 'NULL'; + } + } + } + }; + if ( MKDEBUG && $EVAL_ERROR ) { + _d("Problem explaining", $query, $EVAL_ERROR); + } +} + +# Pass in the currently open $dbh (if any), where $current points to ('execute' +# or 'processlist') and whether you want to be connected to the read_only +# server. Get back which server you're looking at, and the $dbh. Assumes that +# one of the servers is ALWAYS read only and the other is ALWAYS not! If +# there's some transition period where this isn't true, maybe both will end up +# pointing to the same place, but that should resolve shortly. +# The magic switching functionality only works if --mirror is given! Otherwise +# it just returns the correct $dbh. $comment is some descriptive text for +# debuggin, like 'for --execute'. +sub find_role { + my ( $o, $dbh, $current, $read_only, $comment ) = @_; + if ( !$dbh || !$dbh->ping ) { + MKDEBUG && _d('Getting a dbh from', $current, $comment); + $dbh = $dp->get_dbh( + $dp->get_cxn_params($o->get($current)), {AutoCommit => 1}); + $dbh->{InactiveDestroy} = 1; # Don't die on fork(). + } + if ( $o->get('mirror') ) { + my ( $is_read_only ) = $dbh->selectrow_array('SELECT @@global.read_only'); + MKDEBUG && _d("read_only on", $current, $comment, ':', + $is_read_only, '(want', $read_only, ')'); + if ( $is_read_only != $read_only ) { + $current = $current eq 'execute' ? 'processlist' : 'execute'; + MKDEBUG && _d("read_only wrong", $comment, "getting a dbh from", $current); + $dbh = $dp->get_dbh( + $dp->get_cxn_params($o->get($current)), {AutoCommit => 1}); + $dbh->{InactiveDestroy} = 1; # Don't die on fork(). + } + } + return ($current, $dbh); +} + +# Catches signals so we can exit gracefully. +sub sig_int { + my ( $signal ) = @_; + if ( $oktorun ) { + print STDERR "# Caught SIG$signal.\n"; + $oktorun = 0; + } + else { + print STDERR "# Exiting on SIG$signal.\n"; + exit(1); + } +} + +sub make_alt_attrib { + my ( $alt_attrib ) = @_; + my @alts = split('\|', $alt_attrib); + my $attrib = shift @alts; + MKDEBUG && _d('Primary attrib:', $attrib, 'aliases:', @alts); + my @lines; + push @lines, + 'sub { my ( $event ) = @_; ', + 'MKDEBUG && _d("callback: alt attrib");', + "if ( exists \$event->{'$attrib'} ) { ", + (map { "delete \$event->{'$_'}; "; } @alts), + 'return $event; }', + # Primary attrib doesn't exist; look for alts + (map { + "if ( exists \$event->{'$_'} ) { " + . "\$event->{'$attrib'} = \$event->{'$_'}; " + . "delete \$event->{'$_'}; " + . 'return $event; }'; + } @alts), + 'return $event; }'; + MKDEBUG && _d('attrib alias sub for', $attrib, ':', @lines); + my $sub = eval join("\n", @lines); + die if $EVAL_ERROR; + return $sub; +} + +sub make_inherit_attribs { + my ( $attribs, $prev ) = @_; + my @lines; + push @lines, + 'sub { my ( $event ) = @_; ', + 'MKDEBUG && _d("callback: inherit attribs");', + (map { + "if ( defined \$event->{'$_'} ) { \$prev->{'$_'} = \$event->{'$_'} } else { \$event->{'$_'} = \$prev->{'$_'}; }" + } @$attribs), + 'return $event; }'; + MKDEBUG && _d('inherit attribs sub:', @lines); + my $sub = eval join("\n", @lines); + die if $EVAL_ERROR; + return $sub; +} + +# Checks that the orderby attrib exists in the ea, returns the default +# orderby attrib if not. +sub check_orderby_attrib { + my ( $orderby_attrib, $ea, $o ) = @_; + + if ( !$ea->type_for($orderby_attrib) && $orderby_attrib ne 'Query_time' ) { + my $default_orderby = $o->get_defaults()->{'order-by'}; + + # Print the notice only if the query report is being printed, too. + if ( $o->get('report-format')->{query_report} ) { + print "--order-by attribute $orderby_attrib doesn't exist, " + . "using $default_orderby\n"; + } + + # Fall back to the default orderby attrib. + ( $orderby_attrib, undef ) = split(/:/, $default_orderby); + } + + MKDEBUG && _d('orderby attrib:', $orderby_attrib); + return $orderby_attrib; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +# ############################################################################ +# Run the program. +# ############################################################################ +if ( !caller ) { exit main(@ARGV); } + +1; # Because this is a module as well as a script. + +# ############################################################################# +# Documentation. +# ############################################################################# + +=pod + +=head1 NAME + +mk-query-digest - Parses logs and more. Analyze, transform, filter, review and +report on queries. + +=head1 SYNOPSIS + +Analyze and report on a slow log: + + mk-query-digest /path/to/slow.log + +Review a slow log, saving results to the test.query_review table in a MySQL +server running on host1. See L<"--review"> for more on reviewing queries: + + mk-query-digest --review h=host1,D=test,t=query_review /path/to/slow.log + +Watch a server's SHOW FULL PROCESSLIST and analyze the queries as if they were +from a slow query log: + + mk-query-digest --processlist h=host1 + +Watch a server's SHOW FULL PROCESSLIST, filter out everything but SELECT +queries, and replay the queries against another server, then use the timings +from replaying them to analyze their performance: + + mk-query-digest --processlist h=host1 --execute h=another_server \ + --filter '$event->{fingerprint} =~ m/^select/' + +=head1 RISKS + +The following section is included to inform users about the potential risks, +whether known or unknown, of using this tool. The two main categories of risks +are those created by the nature of the tool (e.g. read-only tools vs. read-write +tools) and those created by bugs. + +By default mk-query-digest only reads information. The L<"--execute"> option, +however, can cause mk-query-digest to modify and delete data. mk-query-digest +will write to the L<"--review"> and L<"--review-history"> tables if those +options are used. Parsing huge log files may cause significant CPU and memory +usage. + +At the time of this release, there is a bug with L<"--processlist"> and +delayed replication threads, a bug causing certain queries to not be +rewritten correctly as a SELECT, and a bug which causes crashes on OpenBSD. + +The authoritative source for updated information is always the online issue +tracking system. Issues that affect this tool will be marked as such. You can +see a list of such issues at the following URL: +L. + +See also L<"BUGS"> for more information on filing bugs and getting help. + +=head1 DESCRIPTION + +This tool was formerly known as mk-log-parser. + +C is a framework for doing things with events from a query +source such as the slow query log or PROCESSLIST. By default it acts as a very +sophisticated log analysis tool. You can group and sort queries in many +different ways simultaneously and find the most expensive queries, or create a +timeline of queries in the log, for example. It can also do a "query review," +which means to save a sample of each type of query into a MySQL table so you can +easily see whether you've reviewed and analyzed a query before. The benefit of +this is that you can keep track of changes to your server's queries and avoid +repeated work. You can also save other information with the queries, such as +comments, issue numbers in your ticketing system, and so on. + +Note that this is a work in *very* active progress and you should expect +incompatible changes in the future. + +=head1 ATTRIBUTES + +mk-query-digest works on events, which are a collection of key/value pairs +called attributes. You'll recognize most of the attributes right away: +Query_time, Lock_time, and so on. You can just look at a slow log and see them. +However, there are some that don't exist in the slow log, and slow logs +may actually include different kinds of attributes (for example, you may have a +server with the Percona patches). + +For a full list of attributes, see +L. + +=head2 memcached + +memcached events have additional attributes related to the memcached protocol: +cmd, key, res (result) and val. Also, boolean attributes are created for +the various commands, misses and errors: Memc_CMD where CMD is a memcached +command (get, set, delete, etc.), Memc_error and Memc_miss. + +These attributes are no different from slow log attributes, so you can use them +with L<"--[no]report">, L<"--group-by">, in a L<"--filter">, etc. + +These attributes and more are documented at +L. + +=head1 OUTPUT + +The default output is a query analysis report. The L<"--[no]report"> option +controls whether or not this report is printed. Sometimes you may wish to +parse all the queries but suppress the report, for example when using +L<"--print"> or L<"--review">. + +There is one paragraph for each class of query analyzed. A "class" of queries +all have the same value for the L<"--group-by"> attribute which is +"fingerprint" by default. (See L<"ATTRIBUTES">.) A fingerprint is an +abstracted version of the query text with literals removed, whitespace +collapsed, and so forth. The report is formatted so it's easy to paste into +emails without wrapping, and all non-query lines begin with a comment, so you +can save it to a .sql file and open it in your favorite syntax-highlighting +text editor. There is a response-time profile at the very end. + +The report begins with one paragraph about the entire analysis run. The +information is very similar to what you'll see for each class of queries in the +log, but it doesn't have some information that would be too expensive to keep +globally for the analysis. It also has some statistics about the code's +excution itself, such as the CPU and memory usage. + +Following this, each query then appears in a paragraph. Here's a sample, +slightly reformatted so 'perldoc' will not wrap lines in a terminal. The +following will all be one paragraph, but we'll break it up for commentary. + + # Query 2: 0.01 QPS, 0.02x conc, ID 0xFDEA8D2993C9CAF3 at byte 160665 + +This line identifies the sequential number of the query in the sort order +specified by L<"--order-by">. Then there's the queries per second, and the +approximate concurrency for this query (calculated as a function of the timespan +and total Query_time). Next there's a query ID. This ID is a hex version of +the query's checksum in the database, if you're using L<"--review">. You can +select the reviewed query's details from the database with a query like C field with the info which is otherwise appended +; to URLs. If you want XHTML conformity, remove the form entry. +; Note that all valid entries require a "=", even if no value follows. +url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=,fieldset=" + +[MSSQL] +; Allow or prevent persistent links. +mssql.allow_persistent = On + +; Maximum number of persistent links. -1 means no limit. +mssql.max_persistent = -1 + +; Maximum number of links (persistent+non persistent). -1 means no limit. +mssql.max_links = -1 + +; Minimum error severity to display. +mssql.min_error_severity = 10 + +; Minimum message severity to display. +mssql.min_message_severity = 10 + +; Compatibility mode with old versions of PHP 3.0. +mssql.compatability_mode = Off + +; Connect timeout +;mssql.connect_timeout = 5 + +; Query timeout +;mssql.timeout = 60 + +; Valid range 0 - 2147483647. Default = 4096. +;mssql.textlimit = 4096 + +; Valid range 0 - 2147483647. Default = 4096. +;mssql.textsize = 4096 + +; Limits the number of records in each batch. 0 = all records in one batch. +;mssql.batchsize = 0 + +; Specify how datetime and datetim4 columns are returned +; On => Returns data converted to SQL server settings +; Off => Returns values as YYYY-MM-DD hh:mm:ss +;mssql.datetimeconvert = On + +; Use NT authentication when connecting to the server +mssql.secure_connection = Off + +; Specify max number of processes. -1 = library default +; msdlib defaults to 25 +; FreeTDS defaults to 4096 +;mssql.max_procs = -1 + +; Specify client character set. +; If empty or not set the client charset from freetds.comf is used +; This is only used when compiled with FreeTDS +;mssql.charset = "ISO-8859-1" + +[Assertion] +; Assert(expr); active by default. +;assert.active = On + +; Issue a PHP warning for each failed assertion. +;assert.warning = On + +; Don't bail out by default. +;assert.bail = Off + +; User-function to be called if an assertion fails. +;assert.callback = 0 + +; Eval the expression with current error_reporting(). Set to true if you want +; error_reporting(0) around the eval(). +;assert.quiet_eval = 0 + +[COM] +; path to a file containing GUIDs, IIDs or filenames of files with TypeLibs +;com.typelib_file = +; allow Distributed-COM calls +;com.allow_dcom = true +; autoregister constants of a components typlib on com_load() +;com.autoregister_typelib = true +; register constants casesensitive +;com.autoregister_casesensitive = false +; show warnings on duplicate constant registrations +;com.autoregister_verbose = true + +[mbstring] +; language for internal character representation. +;mbstring.language = Japanese + +; internal/script encoding. +; Some encoding cannot work as internal encoding. +; (e.g. SJIS, BIG5, ISO-2022-*) +;mbstring.internal_encoding = EUC-JP + +; http input encoding. +;mbstring.http_input = auto + +; http output encoding. mb_output_handler must be +; registered as output buffer to function +;mbstring.http_output = SJIS + +; enable automatic encoding translation according to +; mbstring.internal_encoding setting. Input chars are +; converted to internal encoding by setting this to On. +; Note: Do _not_ use automatic encoding translation for +; portable libs/applications. +;mbstring.encoding_translation = Off + +; automatic encoding detection order. +; auto means +;mbstring.detect_order = auto + +; substitute_character used when character cannot be converted +; one from another +;mbstring.substitute_character = none; + +; overload(replace) single byte functions by mbstring functions. +; mail(), ereg(), etc are overloaded by mb_send_mail(), mb_ereg(), +; etc. Possible values are 0,1,2,4 or combination of them. +; For example, 7 for overload everything. +; 0: No overload +; 1: Overload mail() function +; 2: Overload str*() functions +; 4: Overload ereg*() functions +;mbstring.func_overload = 0 + +[FrontBase] +;fbsql.allow_persistent = On +;fbsql.autocommit = On +;fbsql.show_timestamp_decimals = Off +;fbsql.default_database = +;fbsql.default_database_password = +;fbsql.default_host = +;fbsql.default_password = +;fbsql.default_user = "_SYSTEM" +;fbsql.generate_warnings = Off +;fbsql.max_connections = 128 +;fbsql.max_links = 128 +;fbsql.max_persistent = -1 +;fbsql.max_results = 128 + +[gd] +; Tell the jpeg decode to libjpeg warnings and try to create +; a gd image. The warning will then be displayed as notices +; disabled by default +;gd.jpeg_ignore_warning = 0 + +[exif] +; Exif UNICODE user comments are handled as UCS-2BE/UCS-2LE and JIS as JIS. +; With mbstring support this will automatically be converted into the encoding +; given by corresponding encode setting. When empty mbstring.internal_encoding +; is used. For the decode settings you can distinguish between motorola and +; intel byte order. A decode setting cannot be empty. +;exif.encode_unicode = ISO-8859-15 +;exif.decode_unicode_motorola = UCS-2BE +;exif.decode_unicode_intel = UCS-2LE +;exif.encode_jis = +;exif.decode_jis_motorola = JIS +;exif.decode_jis_intel = JIS + +[Tidy] +; The path to a default tidy configuration file to use when using tidy +;tidy.default_config = /usr/local/lib/php/default.tcfg + +; Should tidy clean and repair output automatically? +; WARNING: Do not use this option if you are generating non-html content +; such as dynamic images +tidy.clean_output = Off + +[soap] +; Enables or disables WSDL caching feature. +soap.wsdl_cache_enabled=1 +; Sets the directory name where SOAP extension will put cache files. +soap.wsdl_cache_dir="/tmp" +; (time to live) Sets the number of second while cached file will be used +; instead of original one. +soap.wsdl_cache_ttl=86400 + +; Local Variables: +; tab-width: 4 +; End: diff --git a/php/metadata.json b/php/metadata.json new file mode 100644 index 0000000..47a00e4 --- /dev/null +++ b/php/metadata.json @@ -0,0 +1,101 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs and maintains php and php modules", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "php::module_fpdf": "Install the php-fpdf package", + "php::php5-cgi": "Install the php5-cgi package", + "php::module_sqlite3": "Install the php5-sqlite3 package", + "php::module_gd": "Install the php5-gd package", + "php::module_ldap": "Install the php5-ldap package", + "php": "", + "php::module_curl": "Install the php5-curl package", + "php::module_mysql": "Install the php5-mysql package", + "php::module_apc": "Install the php5-apc package", + "php::pear": "Install the php-pear package", + "php::module_memcache": "Install the php5-memcache package", + "php::php4": "Install packages for PHP version 4", + "php::module_fileinfo": "Install the php5-fileinfo package", + "php::module_pgsql": "Install the php5-pgsql packag", + "php::php5": "Install php5 packages and php.ini config file" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "php", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "php::module_fpdf": [ + + ], + "php::php5-cgi": [ + + ], + "php": [ + + ], + "php::module_sqlite3": [ + + ], + "php::module_gd": [ + + ], + "php::module_ldap": [ + + ], + "php::module_curl": [ + + ], + "php::module_mysql": [ + + ], + "php::module_apc": [ + + ], + "php::pear": [ + + ], + "php::module_memcache": [ + + ], + "php::php4": [ + + ], + "php::module_fileinfo": [ + + ], + "php::module_pgsql": [ + + ], + "php::php5": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + "apache2": [ + + ] + } +} \ No newline at end of file diff --git a/php/metadata.rb b/php/metadata.rb new file mode 100644 index 0000000..7e592dc --- /dev/null +++ b/php/metadata.rb @@ -0,0 +1,24 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and maintains php and php modules" +version "0.7" +depends "apache2" +recipe "php::module_apc", "Install the php5-apc package" +recipe "php::module_curl", "Install the php5-curl package" +recipe "php::module_fileinfo", "Install the php5-fileinfo package" +recipe "php::module_fpdf", "Install the php-fpdf package" +recipe "php::module_gd", "Install the php5-gd package" +recipe "php::module_ldap", "Install the php5-ldap package" +recipe "php::module_memcache", "Install the php5-memcache package" +recipe "php::module_mysql", "Install the php5-mysql package" +recipe "php::module_pgsql", "Install the php5-pgsql packag" +recipe "php::module_sqlite3", "Install the php5-sqlite3 package" +recipe "php::pear", "Install the php-pear package" +recipe "php::php4", "Install packages for PHP version 4" +recipe "php::php5-cgi", "Install the php5-cgi package" +recipe "php::php5", "Install php5 packages and php.ini config file" + +%w{ubuntu debian}.each do |os| + supports os +end diff --git a/php/recipes/default.rb b/php/recipes/default.rb new file mode 100644 index 0000000..25049e3 --- /dev/null +++ b/php/recipes/default.rb @@ -0,0 +1,19 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: php +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/php/recipes/module_apc.rb b/php/recipes/module_apc.rb new file mode 100644 index 0000000..52d37ab --- /dev/null +++ b/php/recipes/module_apc.rb @@ -0,0 +1,23 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: php +# Recipe:: module_apc +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "php5-apc" do + action :upgrade +end diff --git a/php/recipes/module_curl.rb b/php/recipes/module_curl.rb new file mode 100644 index 0000000..468547a --- /dev/null +++ b/php/recipes/module_curl.rb @@ -0,0 +1,23 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: php +# Recipe:: module_curl +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "php5-curl" do + action :upgrade +end diff --git a/php/recipes/module_fileinfo.rb b/php/recipes/module_fileinfo.rb new file mode 100644 index 0000000..f2bf1dc --- /dev/null +++ b/php/recipes/module_fileinfo.rb @@ -0,0 +1,23 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: php +# Recipe:: module_fileinfo +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "php5-fileinfo" do + action :upgrade +end diff --git a/php/recipes/module_fpdf.rb b/php/recipes/module_fpdf.rb new file mode 100644 index 0000000..2c38958 --- /dev/null +++ b/php/recipes/module_fpdf.rb @@ -0,0 +1,23 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: php +# Recipe:: module_fpdf +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "php-fpdf" do + action :upgrade +end diff --git a/php/recipes/module_gd.rb b/php/recipes/module_gd.rb new file mode 100644 index 0000000..deba7c1 --- /dev/null +++ b/php/recipes/module_gd.rb @@ -0,0 +1,23 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: php +# Recipe:: module_gd +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "php5-gd" do + action :upgrade +end diff --git a/php/recipes/module_ldap.rb b/php/recipes/module_ldap.rb new file mode 100644 index 0000000..fd64bbd --- /dev/null +++ b/php/recipes/module_ldap.rb @@ -0,0 +1,23 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: php +# Recipe:: module_ldap +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "php5-ldap" do + action :upgrade +end diff --git a/php/recipes/module_memcache.rb b/php/recipes/module_memcache.rb new file mode 100644 index 0000000..250a417 --- /dev/null +++ b/php/recipes/module_memcache.rb @@ -0,0 +1,23 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: php +# Recipe:: module_memcache +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "php5-memcache" do + action :upgrade +end diff --git a/php/recipes/module_mysql.rb b/php/recipes/module_mysql.rb new file mode 100644 index 0000000..70533a1 --- /dev/null +++ b/php/recipes/module_mysql.rb @@ -0,0 +1,23 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: php +# Recipe:: module_mysql +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "php5-mysql" do + action :upgrade +end diff --git a/php/recipes/module_pgsql.rb b/php/recipes/module_pgsql.rb new file mode 100644 index 0000000..5906ad2 --- /dev/null +++ b/php/recipes/module_pgsql.rb @@ -0,0 +1,23 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: php +# Recipe:: module_pgsql +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "php5-pgsql" do + action :upgrade +end diff --git a/php/recipes/module_sqlite3.rb b/php/recipes/module_sqlite3.rb new file mode 100644 index 0000000..15bf1d0 --- /dev/null +++ b/php/recipes/module_sqlite3.rb @@ -0,0 +1,23 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: php +# Recipe:: module_sqlite3 +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "php5-sqlite" do + action :upgrade +end diff --git a/php/recipes/pear.rb b/php/recipes/pear.rb new file mode 100644 index 0000000..5db7d8f --- /dev/null +++ b/php/recipes/pear.rb @@ -0,0 +1,23 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: php +# Recipe:: pear +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "php-pear" do + action :install +end diff --git a/php/recipes/php4.rb b/php/recipes/php4.rb new file mode 100644 index 0000000..198f477 --- /dev/null +++ b/php/recipes/php4.rb @@ -0,0 +1,25 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: php +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +%w{ php4 php4-mysql php4-ldap php4-gd }.each do |pkg| + package pkg do + action :upgrade + end +end diff --git a/php/recipes/php5-cgi.rb b/php/recipes/php5-cgi.rb new file mode 100644 index 0000000..0389c35 --- /dev/null +++ b/php/recipes/php5-cgi.rb @@ -0,0 +1,30 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: php +# Recipe:: php5-cgi +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2" +include_recipe "php::module_mysql" +include_recipe "php::module_sqlite3" +include_recipe "php::module_memcache" +include_recipe "php::module_gd" +include_recipe "php::module_pgsql" + +package "php5-cgi" do + action :upgrade +end diff --git a/php/recipes/php5.rb b/php/recipes/php5.rb new file mode 100644 index 0000000..02acd66 --- /dev/null +++ b/php/recipes/php5.rb @@ -0,0 +1,41 @@ +# +# Author:: Joshua Timberman () +# Cookbook Name:: php +# Recipe:: php5 +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2" +include_recipe "php::module_mysql" +include_recipe "php::module_ldap" +include_recipe "php::module_memcache" +include_recipe "php::module_gd" +include_recipe "php::module_pgsql" +include_recipe "php::pear" + +remote_file value_for_platform([ "centos", "redhat", "suse" ] => {"default" => "/etc/php.ini"}, "default" => "/etc/php5/apache2/php.ini") do + source "apache2-php5.ini" + owner "root" + group "root" + mode 0644 + notifies :restart, resources("service[apache2]"), :delayed +end + +%w{ php5 php5-cli samrty }.each do |pkg| + package pkg do + action :upgrade + end +end diff --git a/php/templates/default/php.conf.erb b/php/templates/default/php.conf.erb new file mode 100644 index 0000000..878eabe --- /dev/null +++ b/php/templates/default/php.conf.erb @@ -0,0 +1,63 @@ + + + DocumentRoot <%= docroot %> + +<% if @node[:virtual_host_name] -%> + ServerName <%= @node[:virtual_host_name] %> +<% end -%> +<% if @node[:virtual_host_alias] -%> +<% va_list = @node[:virtual_host_alias].kind_of?(Array) ? @node[:virtual_host_alias] : [ @node[:virtual_host_alias] ] -%> +<% va_list.each do |va| -%> + ServerAlias <%= va %> +<% end -%> +<% end -%> + + + Options FollowSymLinks + AllowOverride None + + + + SetHandler server-status + + Order Deny,Allow + Deny from all + Allow from 127.0.0.1 + + + LogLevel info + ErrorLog /var/log/apache2/<%= application_name %>-error.log + CustomLog /var/log/apache2/<%= application_name %>-access.log combined + + RewriteEngine On + RewriteLog /var/log/apache2/<%= application_name %>-rewrite.log + RewriteLogLevel 0 + + <% if canonical_hostname != "false" %> + RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f + RewriteCond %{HTTP_HOST} !^<%= canonical_hostname.gsub(/\./, '\.') %> [NC] + RewriteCond %{HTTP_HOST} !^$ + RewriteCond %{HTTP_HOST} !^localhost [NC] + RewriteCond %{HTTP_HOST} !^127\.0\.0\.1 [NC] + RewriteCond %{HTTP_HOST} !^<%= @node[:fqdn].gsub(/\./, '\.') %> [NC] + RewriteCond %{HTTP_HOST} !^<%= @node[:hostname].gsub(/\./, '\.') %> [NC] + RewriteRule ^/(.*) http://<%= canonical_hostname %>/$1 [L,R=301] + <% end %> + + RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f + RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f + RewriteCond %{SCRIPT_FILENAME} !maintenance.html + RewriteRule ^.*$ /system/maintenance.html [L] + + RewriteRule ^/server-status$ /server-status$1 [L] + + > + Options Indexes FollowSymLinks MultiViews + AllowOverride All + Order allow,deny + allow from all + + + AddOutputFilterByType DEFLATE text/html text/plain text/xml + + diff --git a/php5/attributes/php5.rb b/php5/attributes/php5.rb new file mode 100755 index 0000000..9bc2bd1 --- /dev/null +++ b/php5/attributes/php5.rb @@ -0,0 +1,5 @@ +php5 Mash.new unless attribute?("php5") +default[:php5][:version] = "5.3.0" +default[:php5][:path] = "/usr/local/php" +default[:php5][:tar_pkg] = "php-#{php5[:version]}-#{platform}-#{platform_version}-#{kernel[:machine]}.tar.bz2" +default[:php5][:dist_url] = "http://dist/packages/#{php5[:tar_pkg]}" diff --git a/php5/metadata.json b/php5/metadata.json new file mode 100755 index 0000000..04d5d11 --- /dev/null +++ b/php5/metadata.json @@ -0,0 +1,44 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "php5::sites": "", + "php5": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + "apache2": [ + + ] + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures php5", + "version": "0.1.0", + "name": "php5", + "providing": { + "php5::sites": [ + + ], + "php5": [ + + ] + } +} \ No newline at end of file diff --git a/php5/metadata.rb b/php5/metadata.rb new file mode 100755 index 0000000..756fd46 --- /dev/null +++ b/php5/metadata.rb @@ -0,0 +1,5 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures php5" +version "0.1" +depends "apache2" \ No newline at end of file diff --git a/php5/recipes/default.rb b/php5/recipes/default.rb new file mode 100755 index 0000000..95c71eb --- /dev/null +++ b/php5/recipes/default.rb @@ -0,0 +1,32 @@ +unless node[:platform] == "ubuntu" + Chef::Log.warn("This recipe is only available for Ubuntu systems.") + return +end + +package "libmcrypt4" +package "libltdl7" +package "apache2-mpm-worker" +package "libapache2-mod-fcgid" +apache_module "fcgid" + +bash "install_php" do + code <<-EOC +wget #{node[:php5][:dist_url]} +tar -C /#{node[:php5][:path].split("/")[1..-2].join("/")} -xpf #{node[:php5][:tar_pkg]} +EOC + user "root" + cwd "/tmp" + not_if { File.directory?(node[:php5][:path]) } +end + +link "/usr/bin/php-cgi" do + to "/usr/local/php/bin/php-cgi" +end + +template "/usr/local/php/lib/php.ini" do + source "php.ini.erb" + owner "root" + group "root" + mode 0644 +end + diff --git a/php5/recipes/sites.rb b/php5/recipes/sites.rb new file mode 100755 index 0000000..f84ca31 --- /dev/null +++ b/php5/recipes/sites.rb @@ -0,0 +1,39 @@ +require_recipe "php5::default" + +directory "/u/logs" do + mode 0755 + owner "root" + group "root" +end + +directory "/u/logs/sites" do + mode 0775 + owner "app" + group "www-data" +end + +logrotate "website_logs" do + files "/u/logs/sites/*.log" + frequency "weekly" + restart_command "/etc/init.d/apache2 reload > /dev/null" +end + +if node[:active_sites] + node[:active_sites].each do |site, conf| + + full_name = "#{site}_#{conf[:env]}" + + apache_site full_name do + config_path "/u/sites/#{site}/current/config/apache/#{conf[:env]}.conf" + only_if { File.exists?("/etc/apache2/sites-available/#{full_name}") } + end + + logrotate full_name do + files "/u/sites/#{site}/shared/log/*.log" + frequency "weekly" + restart_command "/etc/init.d/apache2 reload > /dev/null" + end + end +else + Chef::Log.info "Add an 'active_sites' attribute to configure this node's sites" +end diff --git a/php5/templates/default/php.ini.erb b/php5/templates/default/php.ini.erb new file mode 100755 index 0000000..40f721d --- /dev/null +++ b/php5/templates/default/php.ini.erb @@ -0,0 +1,3 @@ +date.timezone = 'UTC' +allow_url_include = 1 + diff --git a/php5/templates/default/php5.conf.erb b/php5/templates/default/php5.conf.erb new file mode 100755 index 0000000..04fc357 --- /dev/null +++ b/php5/templates/default/php5.conf.erb @@ -0,0 +1,4 @@ + + AddType application/x-httpd-php .php .phtml .php3 + AddType application/x-httpd-php-source .phps + diff --git a/php5/templates/default/php5.load.erb b/php5/templates/default/php5.load.erb new file mode 100755 index 0000000..f015f85 --- /dev/null +++ b/php5/templates/default/php5.load.erb @@ -0,0 +1,3 @@ +LoadModule php5_module <%= @node[:php5][:module_path] %> + + diff --git a/postfix/attributes/postfix.rb b/postfix/attributes/postfix.rb new file mode 100644 index 0000000..9246a44 --- /dev/null +++ b/postfix/attributes/postfix.rb @@ -0,0 +1,14 @@ +set_unless[:postfix][:mail_type] = "client" +set_unless[:postfix][:myhostname] = fqdn +set_unless[:postfix][:mydomain] = domain +set_unless[:postfix][:myorigin] = "$myhostname" +set_unless[:postfix][:relayhost] = "" +set_unless[:postfix][:mail_relay_networks] = "127.0.0.0/8" + +set_unless[:postfix][:smtp_sasl_auth_enable] = "no" +set_unless[:postfix][:smtp_sasl_password_maps] = "hash:/etc/postfix/sasl_passwd" +set_unless[:postfix][:smtp_sasl_security_options] = "noanonymous" +set_unless[:postfix][:smtp_tls_cafile] = "/etc/postfix/cacert.pem" +set_unless[:postfix][:smtp_use_tls] = "yes" +set_unless[:postfix][:smtp_sasl_user_name] = "" +set_unless[:postfix][:smtp_sasl_passwd] = "" diff --git a/postfix/metadata.json b/postfix/metadata.json new file mode 100644 index 0000000..961ec28 --- /dev/null +++ b/postfix/metadata.json @@ -0,0 +1,199 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs and configures postfix for client or outbound relayhost, or to do SASL auth", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "postfix": "", + "postfix::sasl_auth": "Set up postfix to auth to a server with sasl" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "postfix", + "conflicting": { + + }, + "attributes": { + "postfix\/smtp_sasl_password_maps": { + "default": "hash:\/etc\/postfix\/sasl_passwd", + "type": "string", + "multiple_values": false, + "description": "hashmap of SASL passwords", + "display_name": "Postfix SMTP SASL Password Maps", + "recipes": [ + + ], + "required": false + }, + "postfix\/smtp_sasl_security_options": { + "default": "noanonymous", + "type": "string", + "multiple_values": false, + "description": "Sets the value of smtp_sasl_security_options in main.cf", + "display_name": "Postfix SMTP SASL Security Options", + "recipes": [ + + ], + "required": false + }, + "postfix\/mail_relay_networks": { + "default": "127.0.0.0\/8", + "type": "string", + "multiple_values": false, + "description": "Sets the mynetworks value in main.cf", + "display_name": "Postfix Mail Relay Networks", + "recipes": [ + + ], + "required": false + }, + "postfix\/myorigin": { + "default": "$myhostname", + "type": "string", + "multiple_values": false, + "description": "Sets the myorigin value in main.cf", + "display_name": "Postfix Myorigin", + "recipes": [ + + ], + "required": false + }, + "postfix\/myhostname": { + "default": "fqdn", + "type": "string", + "multiple_values": false, + "description": "Sets the myhostname value in main.cf", + "display_name": "Postfix Myhostname", + "recipes": [ + + ], + "required": false + }, + "postfix\/smtp_sasl_user_name": { + "default": "", + "type": "string", + "multiple_values": false, + "description": "User to auth SMTP via SASL", + "display_name": "Postfix SMTP SASL Username", + "recipes": [ + + ], + "required": false + }, + "postfix\/smtp_tls_cafile": { + "default": "\/etc\/postfix\/cacert.pem", + "type": "string", + "multiple_values": false, + "description": "CA certificate file for SMTP over TLS", + "display_name": "Postfix SMTP TLS CA File", + "recipes": [ + + ], + "required": false + }, + "postfix\/smtp_sasl_passwd": { + "default": "", + "type": "string", + "multiple_values": false, + "description": "Password for smtp_sasl_user_name", + "display_name": "Postfix SMTP SASL Password", + "recipes": [ + + ], + "required": false + }, + "postfix\/mail_type": { + "default": "client", + "type": "string", + "multiple_values": false, + "description": "Is this node a client or server?", + "display_name": "Postfix Mail Type", + "recipes": [ + + ], + "required": false + }, + "postfix\/smtp_use_tls": { + "default": "yes", + "type": "string", + "multiple_values": false, + "description": "Whether SMTP SASL Auth should use TLS encryption", + "display_name": "Postfix SMTP Use TLS?", + "recipes": [ + + ], + "required": false + }, + "postfix\/relayhost": { + "default": "", + "type": "string", + "multiple_values": false, + "description": "Sets the relayhost value in main.cf", + "display_name": "Postfix Relayhost", + "recipes": [ + + ], + "required": false + }, + "postfix\/smtp_sasl_auth_enable": { + "default": "no", + "type": "string", + "multiple_values": false, + "description": "Enable SMTP SASL Authentication", + "display_name": "Postfix SMTP SASL Auth Enable", + "recipes": [ + + ], + "required": false + }, + "postfix\/mydomain": { + "default": "domain", + "type": "string", + "multiple_values": false, + "description": "Sets the mydomain value in main.cf", + "display_name": "Postfix Mydomain", + "recipes": [ + + ], + "required": false + }, + "postfix": { + "type": "hash", + "multiple_values": false, + "description": "Hash of Postfix attributes", + "display_name": "Postfix", + "recipes": [ + + ], + "required": false + } + }, + "providing": { + "postfix::sasl_auth": [ + + ], + "postfix": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/postfix/metadata.rb b/postfix/metadata.rb new file mode 100644 index 0000000..18d8032 --- /dev/null +++ b/postfix/metadata.rb @@ -0,0 +1,81 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures postfix for client or outbound relayhost, or to do SASL auth" +version "0.7" +recipe "postfix::sasl_auth", "Set up postfix to auth to a server with sasl" + +%w{ubuntu debian}.each do |os| + supports os +end + +attribute "postfix", + :display_name => "Postfix", + :description => "Hash of Postfix attributes", + :type => "hash" + +attribute "postfix/mail_type", + :display_name => "Postfix Mail Type", + :description => "Is this node a client or server?", + :default => "client" + +attribute "postfix/myhostname", + :display_name => "Postfix Myhostname", + :description => "Sets the myhostname value in main.cf", + :default => "fqdn" + +attribute "postfix/mydomain", + :display_name => "Postfix Mydomain", + :description => "Sets the mydomain value in main.cf", + :default => "domain" + +attribute "postfix/myorigin", + :display_name => "Postfix Myorigin", + :description => "Sets the myorigin value in main.cf", + :default => "$myhostname" + +attribute "postfix/relayhost", + :display_name => "Postfix Relayhost", + :description => "Sets the relayhost value in main.cf", + :default => "" + +attribute "postfix/mail_relay_networks", + :display_name => "Postfix Mail Relay Networks", + :description => "Sets the mynetworks value in main.cf", + :default => "127.0.0.0/8" + +attribute "postfix/smtp_sasl_auth_enable", + :display_name => "Postfix SMTP SASL Auth Enable", + :description => "Enable SMTP SASL Authentication", + :default => "no" + +attribute "postfix/smtp_sasl_password_maps", + :display_name => "Postfix SMTP SASL Password Maps", + :description => "hashmap of SASL passwords", + :default => "hash:/etc/postfix/sasl_passwd" + +attribute "postfix/smtp_sasl_security_options", + :display_name => "Postfix SMTP SASL Security Options", + :description => "Sets the value of smtp_sasl_security_options in main.cf", + :default => "noanonymous" + +attribute "postfix/smtp_tls_cafile", + :display_name => "Postfix SMTP TLS CA File", + :description => "CA certificate file for SMTP over TLS", + :default => "/etc/postfix/cacert.pem" + +attribute "postfix/smtp_use_tls", + :display_name => "Postfix SMTP Use TLS?", + :description => "Whether SMTP SASL Auth should use TLS encryption", + :default => "yes" + +attribute "postfix/smtp_sasl_user_name", + :display_name => "Postfix SMTP SASL Username", + :description => "User to auth SMTP via SASL", + :default => "" + +attribute "postfix/smtp_sasl_passwd", + :display_name => "Postfix SMTP SASL Password", + :description => "Password for smtp_sasl_user_name", + :default => "" + diff --git a/postfix/recipes/default.rb b/postfix/recipes/default.rb new file mode 100644 index 0000000..5c97fe5 --- /dev/null +++ b/postfix/recipes/default.rb @@ -0,0 +1,37 @@ +# +# Author:: Joshua Timberman() +# Cookbook Name:: postfix +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "postfix" do + action :install +end + +service "postfix" do + action :enable +end + +%w{main master}.each do |cfg| + template "/etc/postfix/#{cfg}.cf" do + source "#{cfg}.cf.erb" + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "postfix") + end +end diff --git a/postfix/recipes/sasl_auth.rb b/postfix/recipes/sasl_auth.rb new file mode 100644 index 0000000..7ea922d --- /dev/null +++ b/postfix/recipes/sasl_auth.rb @@ -0,0 +1,40 @@ +# +# Author:: Joshua Timberman() +# Cookbook Name:: postfix +# Recipe:: sasl_auth +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +%w{ libsasl2-2 ca-certificates}.each do |pkg| + package pkg do + action :install + end +end + +execute "postmap-sasl_passwd" do + command "postmap /etc/postfix/sasl_passwd" + action :nothing +end + +template "/etc/postfix/sasl_passwd" do + source "sasl_passwd.erb" + owner "root" + group "root" + mode 0400 + notifies :run, resources(:execute => "postmap-sasl_passwd"), :immediately + notifies :restart, resources(:service => "postfix") +end + diff --git a/postfix/templates/default/main.cf.erb b/postfix/templates/default/main.cf.erb new file mode 100644 index 0000000..0e8144f --- /dev/null +++ b/postfix/templates/default/main.cf.erb @@ -0,0 +1,37 @@ +### +# Generated by Chef for <%= @node[:fqdn] %> +# Configured as <%= @node[:postfix][:mail_type] %> +### + +biff = no +append_dot_mydomain = no +smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem +smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key +smtpd_use_tls=yes +smtpd_tls_session_cache_database = btree:${queue_directory}/smtpd_scache +smtp_tls_session_cache_database = btree:${queue_directory}/smtp_scache +smtp_sasl_auth_enable = <%= @node[:postfix][:smtp_sasl_auth_enable] %> +<% if @node[:postfix][:smtp_sasl_auth_enable] == "yes" -%> +smtp_sasl_password_maps = <%= @node[:postfix][:smtp_sasl_password_maps] %> +smtp_sasl_security_options = <%= @node[:postfix][:smtp_sasl_security_options] %> +smtp_tls_CAfile = <%= @node[:postfix][:smtp_tls_cafile] %> +smtp_use_tls = <%= @node[:postfix][:smtp_use_tls] %> +<% end -%> +myhostname = <%= @node[:postfix][:myhostname] %> +mydomain = <%= @node[:postfix][:mydomain] %> +myorigin = <%= @node[:postfix][:myorigin] %> +smtpd_banner = $myhostname ESMTP $mail_name +alias_maps = hash:/etc/aliases +alias_database = hash:/etc/aliases +mydestination = <%= @node[:postfix][:myhostname] %>, <%= @node[:hostname] %>, localhost.localdomain, localhost +<% if @node[:postfix][:mail_type] == "master" -%> +relayhost = +mynetworks = <%= @node[:postfix][:mail_relay_networks] %> +inet_interfaces = all +<% else -%> +relayhost = <%= @node[:postfix][:relayhost] %> +mynetworks = <%= @node[:postfix][:mail_relay_networks] %> +inet_interfaces = loopback-only +<% end -%> +mailbox_size_limit = 0 +recipient_delimiter = + diff --git a/postfix/templates/default/master.cf.erb b/postfix/templates/default/master.cf.erb new file mode 100644 index 0000000..89b7f18 --- /dev/null +++ b/postfix/templates/default/master.cf.erb @@ -0,0 +1,79 @@ +# +# Postfix master process configuration file. For details on the format +# of the file, see the master(5) manual page (command: "man 5 master"). +# +# ========================================================================== +# service type private unpriv chroot wakeup maxproc command + args +# (yes) (yes) (yes) (never) (100) +# ========================================================================== +smtp inet n - n - - smtpd +#submission inet n - n - - smtpd +# -o smtpd_enforce_tls=yes +# -o smtpd_sasl_auth_enable=yes +# -o smtpd_client_restrictions=permit_sasl_authenticated,reject +#smtps inet n - n - - smtpd +# -o smtpd_tls_wrappermode=yes +# -o smtpd_sasl_auth_enable=yes +# -o smtpd_client_restrictions=permit_sasl_authenticated,reject +#628 inet n - n - - qmqpd +pickup fifo n - n 60 1 pickup +cleanup unix n - n - 0 cleanup +qmgr fifo n - n 300 1 qmgr +#qmgr fifo n - n 300 1 oqmgr +tlsmgr unix - - n 1000? 1 tlsmgr +rewrite unix - - n - - trivial-rewrite +bounce unix - - n - 0 bounce +defer unix - - n - 0 bounce +trace unix - - n - 0 bounce +verify unix - - n - 1 verify +flush unix n - n 1000? 0 flush +proxymap unix - - n - - proxymap +smtp unix - - n - 500 smtp +# When relaying mail as backup MX, disable fallback_relay to avoid MX loops +relay unix - - n - - smtp + -o fallback_relay= +# -o smtp_helo_timeout=5 -o smtp_connect_timeout=5 +showq unix n - n - - showq +error unix - - n - - error +discard unix - - n - - discard +local unix - n n - - local +virtual unix - n n - - virtual +lmtp unix - - n - - lmtp +anvil unix - - n - 1 anvil +scache unix - - n - 1 scache +# +# ==================================================================== +# Interfaces to non-Postfix software. Be sure to examine the manual +# pages of the non-Postfix software to find out what options it wants. +# +# Many of the following services use the Postfix pipe(8) delivery +# agent. See the pipe(8) man page for information about ${recipient} +# and other message envelope options. +# ==================================================================== +# +# maildrop. See the Postfix MAILDROP_README file for details. +# Also specify in main.cf: maildrop_destination_recipient_limit=1 +# +maildrop unix - n n - - pipe + flags=DRhu user=vmail argv=/usr/local/bin/maildrop -d ${recipient} +# +# The Cyrus deliver program has changed incompatibly, multiple times. +# +old-cyrus unix - n n - - pipe + flags=R user=cyrus argv=/usr/lib/cyrus-imapd/deliver -e -m ${extension} ${user} +# Cyrus 2.1.5 (Amos Gouaux) +# Also specify in main.cf: cyrus_destination_recipient_limit=1 +cyrus unix - n n - - pipe + user=cyrus argv=/usr/lib/cyrus-imapd/deliver -e -r ${sender} -m ${extension} ${user} +# +# See the Postfix UUCP_README file for configuration details. +# +uucp unix - n n - - pipe + flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient) +# +# Other external delivery methods. +# +ifmail unix - n n - - pipe + flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient) +bsmtp unix - n n - - pipe + flags=Fq. user=foo argv=/usr/local/sbin/bsmtp -f $sender $nexthop $recipient diff --git a/postfix/templates/default/sasl_passwd.erb b/postfix/templates/default/sasl_passwd.erb new file mode 100644 index 0000000..10a4a23 --- /dev/null +++ b/postfix/templates/default/sasl_passwd.erb @@ -0,0 +1 @@ +<%= @node[:postfix][:relayhost] %> <%= @node[:postfix][:smtp_sasl_user_name] %>:<%= @node[:postfix][:smtp_sasl_passwd] %> diff --git a/postgresql/README.rdoc b/postgresql/README.rdoc new file mode 100644 index 0000000..3348946 --- /dev/null +++ b/postgresql/README.rdoc @@ -0,0 +1,45 @@ += DESCRIPTION: + +Installs and configures postgresql client or server. + += REQUIREMENTS: + +== Platform: + +Tested on Ubuntu 8.10, but adapted from Red Hat Enterprise 5.0 based recipes. + +== Cookbooks: + += ATTRIBUTES: + +* postgresql[:dir] - configuration file location. + += USAGE: + +For clients: + + include_recipe "postgresql::client" + +For server: + + include_recipe "postgresql::server" + +(client is already included by server) + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () + +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/postgresql/attributes/postgresql.rb b/postgresql/attributes/postgresql.rb new file mode 100644 index 0000000..fed13ea --- /dev/null +++ b/postgresql/attributes/postgresql.rb @@ -0,0 +1,27 @@ +# +# Cookbook Name:: postgresql +# Attributes:: postgresql +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case platform +when "redhat","centos","fedora","suse" + set[:postgresql][:dir] = "/var/lib/pgsql/data" +when "debian","ubuntu" + set[:postgresql][:dir] = "/etc/postgresql/8.3/main" +else + set[:postgresql][:dir] = "/etc/postgresql/8.3/main" +end diff --git a/postgresql/metadata.json b/postgresql/metadata.json new file mode 100644 index 0000000..2cfeee6 --- /dev/null +++ b/postgresql/metadata.json @@ -0,0 +1,67 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs and configures postgresql for clients or servers", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "postgresql::server": "Installs postgresql server packages, templates", + "postgresql::client": "Installs postgresql client package(s)", + "postgresql": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "rhel": [ + + ], + "centos": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "postgresql", + "conflicting": { + + }, + "attributes": { + "postgresql\/dir": { + "default": "\/etc\/postgresql\/8.3\/main", + "type": "string", + "multiple_values": false, + "description": "Location of the PostgreSQL databases", + "display_name": "PostgreSQL Directory", + "recipes": [ + + ], + "required": false + } + }, + "providing": { + "postgresql::server": [ + + ], + "postgresql::client": [ + + ], + "postgresql": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nInstalls and configures postgresql client or server.\n\n= REQUIREMENTS:\n\n== Platform:\n\nTested on Ubuntu 8.10, but adapted from Red Hat Enterprise 5.0 based recipes.\n\n== Cookbooks:\n\n= ATTRIBUTES: \n\n* postgresql[:dir] - configuration file location.\n\n= USAGE:\n\nFor clients:\n\n include_recipe \"postgresql::client\"\n \nFor server: \n\n include_recipe \"postgresql::server\"\n \n(client is already included by server)\n\n= LICENSE and AUTHOR:\n \nAuthor:: Joshua Timberman ()\n\nCopyright:: 2009, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/postgresql/metadata.rb b/postgresql/metadata.rb new file mode 100644 index 0000000..4721911 --- /dev/null +++ b/postgresql/metadata.rb @@ -0,0 +1,18 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures postgresql for clients or servers" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.7" +recipe "postgresql::client", "Installs postgresql client package(s)" +recipe "postgresql::server", "Installs postgresql server packages, templates" + +%w{rhel centos ubuntu debian}.each do |os| + supports os +end + +attribute "postgresql/dir", + :display_name => "PostgreSQL Directory", + :description => "Location of the PostgreSQL databases", + :default => "/etc/postgresql/8.3/main" + diff --git a/postgresql/recipes/client.rb b/postgresql/recipes/client.rb new file mode 100644 index 0000000..7e6043b --- /dev/null +++ b/postgresql/recipes/client.rb @@ -0,0 +1,25 @@ +# +# Cookbook Name:: postgresql +# Recipe:: client +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node[:platform] +when "ubuntu","debian" + package "postgresql-client" +when "redhat","centos","fedora" + package "postgresql-devel" +end diff --git a/postgresql/recipes/default.rb b/postgresql/recipes/default.rb new file mode 100644 index 0000000..8563c05 --- /dev/null +++ b/postgresql/recipes/default.rb @@ -0,0 +1,18 @@ +# +# Cookbook Name:: postgresql +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/postgresql/recipes/server.rb b/postgresql/recipes/server.rb new file mode 100644 index 0000000..75ada22 --- /dev/null +++ b/postgresql/recipes/server.rb @@ -0,0 +1,47 @@ +#/postgresql.conf. +# Cookbook Name:: postgresql +# Recipe:: server +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "postgresql::client" + +package "postgresql" + +service "postgresql" do + case node[:platform] + when "debian","ubuntu" + service_name "postgresql-8.3" + end + supports :restart => true, :status => true, :reload => true + action :nothing +end + +template "#{node[:postgresql][:dir]}/pg_hba.conf" do + source "pg_hba.conf.erb" + owner "postgres" + group "postgres" + mode 0600 + notifies :reload, resources(:service => "postgresql") +end + +template "#{node[:postgresql][:dir]}/postgresql.conf" do + source "postgresql.conf.erb" + owner "postgres" + group "postgres" + mode 0600 + notifies :restart, resources(:service => "postgresql") +end diff --git a/postgresql/templates/default/pg_hba.conf.erb b/postgresql/templates/default/pg_hba.conf.erb new file mode 100644 index 0000000..524cbb9 --- /dev/null +++ b/postgresql/templates/default/pg_hba.conf.erb @@ -0,0 +1,83 @@ +# PostgreSQL Client Authentication Configuration File +# =================================================== +# +# Refer to the "Client Authentication" section in the +# PostgreSQL documentation for a complete description +# of this file. A short synopsis follows. +# +# This file controls: which hosts are allowed to connect, how clients +# are authenticated, which PostgreSQL user names they can use, which +# databases they can access. Records take one of these forms: +# +# local DATABASE USER METHOD [OPTION] +# host DATABASE USER CIDR-ADDRESS METHOD [OPTION] +# hostssl DATABASE USER CIDR-ADDRESS METHOD [OPTION] +# hostnossl DATABASE USER CIDR-ADDRESS METHOD [OPTION] +# +# (The uppercase items must be replaced by actual values.) +# +# The first field is the connection type: "local" is a Unix-domain socket, +# "host" is either a plain or SSL-encrypted TCP/IP socket, "hostssl" is an +# SSL-encrypted TCP/IP socket, and "hostnossl" is a plain TCP/IP socket. +# +# DATABASE can be "all", "sameuser", "samerole", a database name, or +# a comma-separated list thereof. +# +# USER can be "all", a user name, a group name prefixed with "+", or +# a comma-separated list thereof. In both the DATABASE and USER fields +# you can also write a file name prefixed with "@" to include names from +# a separate file. +# +# CIDR-ADDRESS specifies the set of hosts the record matches. +# It is made up of an IP address and a CIDR mask that is an integer +# (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that specifies +# the number of significant bits in the mask. Alternatively, you can write +# an IP address and netmask in separate columns to specify the set of hosts. +# +# METHOD can be "trust", "reject", "md5", "crypt", "password", "gss", "sspi", +# "krb5", "ident", "pam" or "ldap". Note that "password" sends passwords +# in clear text; "md5" is preferred since it sends encrypted passwords. +# +# OPTION is the ident map or the name of the PAM service, depending on METHOD. +# +# Database and user names containing spaces, commas, quotes and other special +# characters must be quoted. Quoting one of the keywords "all", "sameuser" or +# "samerole" makes the name lose its special character, and just match a +# database or username with that name. +# +# This file is read on server startup and when the postmaster receives +# a SIGHUP signal. If you edit the file on a running system, you have +# to SIGHUP the postmaster for the changes to take effect. You can use +# "pg_ctl reload" to do that. + +# Put your actual configuration here +# ---------------------------------- +# +# If you want to allow non-local connections, you need to add more +# "host" records. In that case you will also need to make PostgreSQL listen +# on a non-local interface via the listen_addresses configuration parameter, +# or via the -i or -h command line switches. +# + + + + +# DO NOT DISABLE! +# If you change this first entry you will need to make sure that the +# database +# super user can access the database using some other method. +# Noninteractive +# access to all databases is required during automatic maintenance +# (autovacuum, daily cronjob, replication, and similar tasks). +# +# Database administrative login by UNIX sockets +local all postgres ident sameuser + +# TYPE DATABASE USER CIDR-ADDRESS METHOD + +# "local" is for Unix domain socket connections only +local all all ident sameuser +# IPv4 local connections: +host all all 127.0.0.1/32 md5 +# IPv6 local connections: +host all all ::1/128 md5 \ No newline at end of file diff --git a/postgresql/templates/default/postgresql.conf.erb b/postgresql/templates/default/postgresql.conf.erb new file mode 100644 index 0000000..ccc760c --- /dev/null +++ b/postgresql/templates/default/postgresql.conf.erb @@ -0,0 +1,493 @@ +# ----------------------------- +# PostgreSQL configuration file +# ----------------------------- +# +# This file consists of lines of the form: +# +# name = value +# +# (The "=" is optional.) Whitespace may be used. Comments are introduced with +# "#" anywhere on a line. The complete list of parameter names and allowed +# values can be found in the PostgreSQL documentation. +# +# The commented-out settings shown in this file represent the default values. +# Re-commenting a setting is NOT sufficient to revert it to the default value; +# you need to reload the server. +# +# This file is read on server startup and when the server receives a SIGHUP +# signal. If you edit the file on a running system, you have to SIGHUP the +# server for the changes to take effect, or use "pg_ctl reload". Some +# parameters, which are marked below, require a server shutdown and restart to +# take effect. +# +# Any parameter can also be given as a command-line option to the server, e.g., +# "postgres -c log_connections=on". Some paramters can be changed at run time +# with the "SET" SQL command. +# +# Memory units: kB = kilobytes MB = megabytes GB = gigabytes +# Time units: ms = milliseconds s = seconds min = minutes h = hours d = days + + +#------------------------------------------------------------------------------ +# FILE LOCATIONS +#------------------------------------------------------------------------------ + +# The default values of these variables are driven from the -D command-line +# option or PGDATA environment variable, represented here as ConfigDir. + +data_directory = '/var/lib/postgresql/8.3/main' # use data in another directory + # (change requires restart) +hba_file = '/etc/postgresql/8.3/main/pg_hba.conf' # host-based authentication file + # (change requires restart) +ident_file = '/etc/postgresql/8.3/main/pg_ident.conf' # ident configuration file + # (change requires restart) + +# If external_pid_file is not explicitly set, no extra PID file is written. +external_pid_file = '/var/run/postgresql/8.3-main.pid' # write an extra PID file + # (change requires restart) + + +#------------------------------------------------------------------------------ +# CONNECTIONS AND AUTHENTICATION +#------------------------------------------------------------------------------ + +# - Connection Settings - + +#listen_addresses = 'localhost' # what IP address(es) to listen on; + # comma-separated list of addresses; + # defaults to 'localhost', '*' = all + # (change requires restart) +port = 5432 # (change requires restart) +max_connections = 100 # (change requires restart) +# Note: Increasing max_connections costs ~400 bytes of shared memory per +# connection slot, plus lock space (see max_locks_per_transaction). You might +# also need to raise shared_buffers to support more connections. +#superuser_reserved_connections = 3 # (change requires restart) +unix_socket_directory = '/var/run/postgresql' # (change requires restart) +#unix_socket_group = '' # (change requires restart) +#unix_socket_permissions = 0777 # begin with 0 to use octal notation + # (change requires restart) +#bonjour_name = '' # defaults to the computer name + # (change requires restart) + +# - Security and Authentication - + +#authentication_timeout = 1min # 1s-600s +ssl = true # (change requires restart) +#ssl_ciphers = 'ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH' # allowed SSL ciphers + # (change requires restart) +#password_encryption = on +#db_user_namespace = off + +# Kerberos and GSSAPI +#krb_server_keyfile = '' # (change requires restart) +#krb_srvname = 'postgres' # (change requires restart, Kerberos only) +#krb_server_hostname = '' # empty string matches any keytab entry + # (change requires restart, Kerberos only) +#krb_caseins_users = off # (change requires restart) +#krb_realm = '' # (change requires restart) + +# - TCP Keepalives - +# see "man 7 tcp" for details + +#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds; + # 0 selects the system default +#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds; + # 0 selects the system default +#tcp_keepalives_count = 0 # TCP_KEEPCNT; + # 0 selects the system default + + +#------------------------------------------------------------------------------ +# RESOURCE USAGE (except WAL) +#------------------------------------------------------------------------------ + +# - Memory - + +shared_buffers = 24MB # min 128kB or max_connections*16kB + # (change requires restart) +#temp_buffers = 8MB # min 800kB +#max_prepared_transactions = 5 # can be 0 or more + # (change requires restart) +# Note: Increasing max_prepared_transactions costs ~600 bytes of shared memory +# per transaction slot, plus lock space (see max_locks_per_transaction). +#work_mem = 1MB # min 64kB +#maintenance_work_mem = 16MB # min 1MB +#max_stack_depth = 2MB # min 100kB + +# - Free Space Map - + +max_fsm_pages = 153600 # min max_fsm_relations*16, 6 bytes each + # (change requires restart) +#max_fsm_relations = 1000 # min 100, ~70 bytes each + # (change requires restart) + +# - Kernel Resource Usage - + +#max_files_per_process = 1000 # min 25 + # (change requires restart) +#shared_preload_libraries = '' # (change requires restart) + +# - Cost-Based Vacuum Delay - + +#vacuum_cost_delay = 0 # 0-1000 milliseconds +#vacuum_cost_page_hit = 1 # 0-10000 credits +#vacuum_cost_page_miss = 10 # 0-10000 credits +#vacuum_cost_page_dirty = 20 # 0-10000 credits +#vacuum_cost_limit = 200 # 1-10000 credits + +# - Background Writer - + +#bgwriter_delay = 200ms # 10-10000ms between rounds +#bgwriter_lru_maxpages = 100 # 0-1000 max buffers written/round +#bgwriter_lru_multiplier = 2.0 # 0-10.0 multipler on buffers scanned/round + + +#------------------------------------------------------------------------------ +# WRITE AHEAD LOG +#------------------------------------------------------------------------------ + +# - Settings - + +#fsync = on # turns forced synchronization on or off +#synchronous_commit = on # immediate fsync at commit +#wal_sync_method = fsync # the default is the first option + # supported by the operating system: + # open_datasync + # fdatasync + # fsync + # fsync_writethrough + # open_sync +#full_page_writes = on # recover from partial page writes +#wal_buffers = 64kB # min 32kB + # (change requires restart) +#wal_writer_delay = 200ms # 1-10000 milliseconds + +#commit_delay = 0 # range 0-100000, in microseconds +#commit_siblings = 5 # range 1-1000 + +# - Checkpoints - + +#checkpoint_segments = 3 # in logfile segments, min 1, 16MB each +#checkpoint_timeout = 5min # range 30s-1h +#checkpoint_completion_target = 0.5 # checkpoint target duration, 0.0 - 1.0 +#checkpoint_warning = 30s # 0 is off + +# - Archiving - + +#archive_mode = off # allows archiving to be done + # (change requires restart) +#archive_command = '' # command to use to archive a logfile segment +#archive_timeout = 0 # force a logfile segment switch after this + # time; 0 is off + + +#------------------------------------------------------------------------------ +# QUERY TUNING +#------------------------------------------------------------------------------ + +# - Planner Method Configuration - + +#enable_bitmapscan = on +#enable_hashagg = on +#enable_hashjoin = on +#enable_indexscan = on +#enable_mergejoin = on +#enable_nestloop = on +#enable_seqscan = on +#enable_sort = on +#enable_tidscan = on + +# - Planner Cost Constants - + +#seq_page_cost = 1.0 # measured on an arbitrary scale +#random_page_cost = 4.0 # same scale as above +#cpu_tuple_cost = 0.01 # same scale as above +#cpu_index_tuple_cost = 0.005 # same scale as above +#cpu_operator_cost = 0.0025 # same scale as above +#effective_cache_size = 128MB + +# - Genetic Query Optimizer - + +#geqo = on +#geqo_threshold = 12 +#geqo_effort = 5 # range 1-10 +#geqo_pool_size = 0 # selects default based on effort +#geqo_generations = 0 # selects default based on effort +#geqo_selection_bias = 2.0 # range 1.5-2.0 + +# - Other Planner Options - + +#default_statistics_target = 10 # range 1-1000 +#constraint_exclusion = off +#from_collapse_limit = 8 +#join_collapse_limit = 8 # 1 disables collapsing of explicit + # JOIN clauses + + +#------------------------------------------------------------------------------ +# ERROR REPORTING AND LOGGING +#------------------------------------------------------------------------------ + +# - Where to Log - + +#log_destination = 'stderr' # Valid values are combinations of + # stderr, csvlog, syslog and eventlog, + # depending on platform. csvlog + # requires logging_collector to be on. + +# This is used when logging to stderr: +#logging_collector = off # Enable capturing of stderr and csvlog + # into log files. Required to be on for + # csvlogs. + # (change requires restart) + +# These are only used if logging_collector is on: +#log_directory = 'pg_log' # directory where log files are written, + # can be absolute or relative to PGDATA +#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern, + # can include strftime() escapes +#log_truncate_on_rotation = off # If on, an existing log file of the + # same name as the new log file will be + # truncated rather than appended to. + # But such truncation only occurs on + # time-driven rotation, not on restarts + # or size-driven rotation. Default is + # off, meaning append to existing files + # in all cases. +#log_rotation_age = 1d # Automatic rotation of logfiles will + # happen after that time. 0 to disable. +#log_rotation_size = 10MB # Automatic rotation of logfiles will + # happen after that much log output. + # 0 to disable. + +# These are relevant when logging to syslog: +#syslog_facility = 'LOCAL0' +#syslog_ident = 'postgres' + + +# - When to Log - + +#client_min_messages = notice # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # log + # notice + # warning + # error + +#log_min_messages = notice # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic + +#log_error_verbosity = default # terse, default, or verbose messages + +#log_min_error_statement = error # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic (effectively off) + +#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements + # and their durations, > 0 logs only + # statements running at least this time. + +#silent_mode = off # DO NOT USE without syslog or + # logging_collector + # (change requires restart) + +# - What to Log - + +#debug_print_parse = off +#debug_print_rewritten = off +#debug_print_plan = off +#debug_pretty_print = off +#log_checkpoints = off +#log_connections = off +#log_disconnections = off +#log_duration = off +#log_hostname = off +log_line_prefix = '%t ' # special values: + # %u = user name + # %d = database name + # %r = remote host and port + # %h = remote host + # %p = process ID + # %t = timestamp without milliseconds + # %m = timestamp with milliseconds + # %i = command tag + # %c = session ID + # %l = session line number + # %s = session start timestamp + # %v = virtual transaction ID + # %x = transaction ID (0 if none) + # %q = stop here in non-session + # processes + # %% = '%' + # e.g. '<%u%%%d> ' +#log_lock_waits = off # log lock waits >= deadlock_timeout +#log_statement = 'none' # none, ddl, mod, all +#log_temp_files = -1 # log temporary files equal or larger + # than specified size; + # -1 disables, 0 logs all temp files +#log_timezone = unknown # actually, defaults to TZ environment + # setting + + +#------------------------------------------------------------------------------ +# RUNTIME STATISTICS +#------------------------------------------------------------------------------ + +# - Query/Index Statistics Collector - + +#track_activities = on +#track_counts = on +#update_process_title = on + + +# - Statistics Monitoring - + +#log_parser_stats = off +#log_planner_stats = off +#log_executor_stats = off +#log_statement_stats = off + + +#------------------------------------------------------------------------------ +# AUTOVACUUM PARAMETERS +#------------------------------------------------------------------------------ + +#autovacuum = on # Enable autovacuum subprocess? 'on' + # requires track_counts to also be on. +#log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and + # their durations, > 0 logs only + # actions running at least that time. +#autovacuum_max_workers = 3 # max number of autovacuum subprocesses +#autovacuum_naptime = 1min # time between autovacuum runs +#autovacuum_vacuum_threshold = 50 # min number of row updates before + # vacuum +#autovacuum_analyze_threshold = 50 # min number of row updates before + # analyze +#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum +#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze +#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum + # (change requires restart) +#autovacuum_vacuum_cost_delay = 20 # default vacuum cost delay for + # autovacuum, -1 means use + # vacuum_cost_delay +#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for + # autovacuum, -1 means use + # vacuum_cost_limit + + +#------------------------------------------------------------------------------ +# CLIENT CONNECTION DEFAULTS +#------------------------------------------------------------------------------ + +# - Statement Behavior - + +#search_path = '"$user",public' # schema names +#default_tablespace = '' # a tablespace name, '' uses the default +#temp_tablespaces = '' # a list of tablespace names, '' uses + # only default tablespace +#check_function_bodies = on +#default_transaction_isolation = 'read committed' +#default_transaction_read_only = off +#session_replication_role = 'origin' +#statement_timeout = 0 # 0 is disabled +#vacuum_freeze_min_age = 100000000 +#xmlbinary = 'base64' +#xmloption = 'content' + +# - Locale and Formatting - + +datestyle = 'iso, mdy' +#timezone = unknown # actually, defaults to TZ environment + # setting +#timezone_abbreviations = 'Default' # Select the set of available time zone + # abbreviations. Currently, there are + # Default + # Australia + # India + # You can create your own file in + # share/timezonesets/. +#extra_float_digits = 0 # min -15, max 2 +#client_encoding = sql_ascii # actually, defaults to database + # encoding + +# These settings are initialized by initdb, but they can be changed. +lc_messages = 'en_US.UTF-8' # locale for system error message + # strings +lc_monetary = 'en_US.UTF-8' # locale for monetary formatting +lc_numeric = 'en_US.UTF-8' # locale for number formatting +lc_time = 'en_US.UTF-8' # locale for time formatting + +# default configuration for text search +default_text_search_config = 'pg_catalog.english' + +# - Other Defaults - + +#explain_pretty_print = on +#dynamic_library_path = '$libdir' +#local_preload_libraries = '' + + +#------------------------------------------------------------------------------ +# LOCK MANAGEMENT +#------------------------------------------------------------------------------ + +#deadlock_timeout = 1s +#max_locks_per_transaction = 64 # min 10 + # (change requires restart) +# Note: Each lock table slot uses ~270 bytes of shared memory, and there are +# max_locks_per_transaction * (max_connections + max_prepared_transactions) +# lock table slots. + + +#------------------------------------------------------------------------------ +# VERSION/PLATFORM COMPATIBILITY +#------------------------------------------------------------------------------ + +# - Previous PostgreSQL Versions - + +#add_missing_from = off +#array_nulls = on +#backslash_quote = safe_encoding # on, off, or safe_encoding +#default_with_oids = off +#escape_string_warning = on +#regex_flavor = advanced # advanced, extended, or basic +#sql_inheritance = on +#standard_conforming_strings = off +#synchronize_seqscans = on + +# - Other Platforms and Clients - + +#transform_null_equals = off + + +#------------------------------------------------------------------------------ +# CUSTOMIZED OPTIONS +#------------------------------------------------------------------------------ + +#custom_variable_classes = '' # list of custom variable class names diff --git a/python/metadata.json b/python/metadata.json new file mode 100644 index 0000000..b34acf8 --- /dev/null +++ b/python/metadata.json @@ -0,0 +1,43 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs python packages", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "python": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "python", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "python": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/python/metadata.rb b/python/metadata.rb new file mode 100644 index 0000000..d134052 --- /dev/null +++ b/python/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs python packages" +version "0.7" + +%w{ debian ubuntu }.each do |os| + supports os +end diff --git a/python/recipes/default.rb b/python/recipes/default.rb new file mode 100644 index 0000000..bc97e7e --- /dev/null +++ b/python/recipes/default.rb @@ -0,0 +1,30 @@ +# +# Cookbook Name:: python +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +package "python" do + action :install +end + +%w{ + dev imaging matplotlib matplotlib-data matplotlib-doc mysqldb + numpy numpy-ext paramiko scipy setuptools sqlite +}.each do |pkg| + package "python-#{pkg}" do + action :install + end +end diff --git a/quick_start/attributes/quick_start.rb b/quick_start/attributes/quick_start.rb new file mode 100644 index 0000000..1ce3289 --- /dev/null +++ b/quick_start/attributes/quick_start.rb @@ -0,0 +1 @@ +deep_thought "If a tree falls in the forest..." diff --git a/quick_start/metadata.json b/quick_start/metadata.json new file mode 100644 index 0000000..03ef2c3 --- /dev/null +++ b/quick_start/metadata.json @@ -0,0 +1,74 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Example cookbook for quick_start wiki document", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "quick_start": "" + }, + "suggestions": { + + }, + "platforms": { + "solaris": [ + + ], + "freebsd": [ + + ], + "ubuntu": [ + + ], + "openbsd": [ + + ], + "fedora": [ + + ], + "macosx": [ + + ], + "centos": [ + + ], + "debian": [ + + ], + "redhat": [ + + ] + }, + "version": "0.7.0", + "name": "quick_start", + "conflicting": { + + }, + "attributes": { + "quick_start\/deep_thought": { + "default": "If a tree falls in the forest...", + "type": "string", + "multiple_values": false, + "description": "A deep thought", + "display_name": "Quick Start Deep Thought", + "recipes": [ + + ], + "required": false + } + }, + "providing": { + "quick_start": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/quick_start/metadata.rb b/quick_start/metadata.rb new file mode 100644 index 0000000..e74eedb --- /dev/null +++ b/quick_start/metadata.rb @@ -0,0 +1,19 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Example cookbook for quick_start wiki document" +version "0.7" + +%w{ + redhat fedora centos + ubuntu debian + macosx freebsd openbsd + solaris +}.each do |os| + supports os +end + +attribute "quick_start/deep_thought", + :display_name => "Quick Start Deep Thought", + :description => "A deep thought", + :default => "If a tree falls in the forest..." diff --git a/quick_start/recipes/default.rb b/quick_start/recipes/default.rb new file mode 100644 index 0000000..dc3d695 --- /dev/null +++ b/quick_start/recipes/default.rb @@ -0,0 +1,24 @@ +# +# Cookbook Name:: quick_start +# Recipe:: default +# +# Copyright 2009, OpsCode +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +template "/tmp/deep_thought.txt" do + source "deep_thought.txt.erb" + variables :deep_thought => node[:deep_thought] + action :create +end diff --git a/quick_start/templates/default/deep_thought.txt.erb b/quick_start/templates/default/deep_thought.txt.erb new file mode 100644 index 0000000..d4c9713 --- /dev/null +++ b/quick_start/templates/default/deep_thought.txt.erb @@ -0,0 +1 @@ +Todays deep thought: <%= @deep_thought %> diff --git a/rabbitmq/metadata.json b/rabbitmq/metadata.json new file mode 100755 index 0000000..81d7fc9 --- /dev/null +++ b/rabbitmq/metadata.json @@ -0,0 +1,52 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs the RabbitMQ AMQP Broker", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "rabbitmq": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "fedora": [ + + ], + "centos": [ + + ], + "debian": [ + + ], + "redhat": [ + + ] + }, + "version": "0.8.0", + "name": "rabbitmq", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "rabbitmq": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/rabbitmq/metadata.rb b/rabbitmq/metadata.rb new file mode 100755 index 0000000..04b1594 --- /dev/null +++ b/rabbitmq/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs the RabbitMQ AMQP Broker" +version "0.8" + +%w{ centos redhat fedora ubuntu debian }.each do |os| + supports os +end diff --git a/rabbitmq/recipes/default.rb b/rabbitmq/recipes/default.rb new file mode 100755 index 0000000..1e57993 --- /dev/null +++ b/rabbitmq/recipes/default.rb @@ -0,0 +1,13 @@ +# Is this valid for all platforms? Is it not just rabbitmq on some platform? +# Valid for ubuntu, *probably* debian and EL5 +# http://download.fedora.redhat.com/pub/epel/5/x86_64/repoview/letter_r.group.html +package "rabbitmq-server" + +service "rabbitmq-server" do + if platform?("centos","redhat","fedora") + start_command "/sbin/service rabbitmq-server start &> /dev/null" + stop_command "/sbin/service rabbitmq-server stop &> /dev/null" + end + supports [ :restart, :status ] + action [ :enable, :start ] +end diff --git a/radiant/README.rdoc b/radiant/README.rdoc new file mode 100644 index 0000000..3418f8b --- /dev/null +++ b/radiant/README.rdoc @@ -0,0 +1,74 @@ += DESCRIPTION: + +Installs RadiantCMS, a Ruby on Rails content management system. + += CHANGELOG: + +Changes to note in this version (from COOK-117). + +* added gem install capabilities +* default to gem install +* removed dependency on ezmobius/chef-deploy (use deploy provider instead) +* fixed tiny error in attributes. +* updated attribute syntax + += REQUIREMENTS: + +== Platform: + +Tested on Ubuntu 9.04, uses the Opscode Apache2 cookbook which is Ubuntu/Debian specific. + +Requires Chef 0.7.12 for Deploy resource when installing from Radiant's git repo. + +== Cookbooks: + +Opscode cookbooks (http://github.com/opscode/cookbooks/tree/master) + +* git +* sqlite +* rails +* apache2 + += ATTRIBUTES: + +* radiant[:edge] - Do a deploy from github repo if true, use gems if false, default false. +* radiant[:branch] - Branch to deploy from, default HEAD. +* radiant[:migrate] - Whether to do a database migration, default false. +* radiant[:migrate_command] - Command to do a database migration, default 'rake db:migrate'. +* radiant[:environment] - Rails environment to use, default is production. +* radiant[:revision] - Revision to deploy, default HEAD. +* radiant[:action] - Whether to deploy, rollback or nothing, default nothing. + += USAGE: + +This recipe uses SQLite3 for the database by default. To set up the default database to get Radiant rolling, run a db:bootstrap by changing the radiant[:migrate] command to the following in the webui: + + yes | rake production db:bootstrap \ + ADMIN_NAME=Administrator \ + ADMIN_USERNAME=admin \ + ADMIN_PASSWORD=radiant \ + DATABASE_TEMPLATE=empty.yml + +Change as required for your environment. If the target system doesn't have /usr/bin/yes, use echo 'yes' instead. + +Radiant supports other database backends. We don't yet have automation ready to set up a database user and grant privileges, or creating the database itself. + += LICENSE and AUTHOR: + + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + diff --git a/radiant/attributes/radiant.rb b/radiant/attributes/radiant.rb new file mode 100644 index 0000000..747cdef --- /dev/null +++ b/radiant/attributes/radiant.rb @@ -0,0 +1,27 @@ +# +# Cookbook Name:: radiant +# Attributes:: radiant +# +# Copyright 2009, Opscode, Inc. +# Copyright 2009, Daniel DeLeo +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set_unless[:radiant][:branch] = "HEAD" +set_unless[:radiant][:migrate] = false +set_unless[:radiant][:migrate_command] = "rake db:migrate" +set_unless[:radiant][:environment] = "production" +set_unless[:radiant][:revision] = "HEAD" +set_unless[:radiant][:action] = "nothing" +set_unless[:radiant][:edge] = false diff --git a/radiant/libraries/radiant.rb b/radiant/libraries/radiant.rb new file mode 100644 index 0000000..ab779bf --- /dev/null +++ b/radiant/libraries/radiant.rb @@ -0,0 +1,7 @@ +class Chef + class Recipe + def radiant_edge? + @node[:radiant][:edge] + end + end +end \ No newline at end of file diff --git a/radiant/metadata.json b/radiant/metadata.json new file mode 100644 index 0000000..4ff6fcd --- /dev/null +++ b/radiant/metadata.json @@ -0,0 +1,125 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs radiant from Git repository", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "radiant": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.9.0", + "name": "radiant", + "conflicting": { + + }, + "attributes": { + "radiant\/action": { + "default": "nothing", + "type": "string", + "multiple_values": false, + "description": "Whether to deploy the application or not", + "display_name": "Radiant Action", + "recipes": [ + + ], + "required": false + }, + "radiant\/revision": { + "default": "HEAD", + "type": "string", + "multiple_values": false, + "description": "Revision to use from Git", + "display_name": "Radiant Revision", + "recipes": [ + + ], + "required": false + }, + "radiant\/environment": { + "default": "production", + "type": "string", + "multiple_values": false, + "description": "Rails environment to use", + "display_name": "Radiant Environment", + "recipes": [ + + ], + "required": false + }, + "radiant\/migrate_command": { + "default": "rake db:migrate", + "type": "string", + "multiple_values": false, + "description": "Command to perform migration", + "display_name": "Radiant Migrate Command", + "recipes": [ + + ], + "required": false + }, + "radiant\/migrate": { + "default": "false", + "type": "string", + "multiple_values": false, + "description": "Whether to do a migration", + "display_name": "Radiant Migrate", + "recipes": [ + + ], + "required": false + }, + "radiant\/branch": { + "default": "HEAD", + "type": "string", + "multiple_values": false, + "description": "Branch from Git to use", + "display_name": "Radiant Branch", + "recipes": [ + + ], + "required": false + } + }, + "providing": { + "radiant": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nInstalls RadiantCMS, a Ruby on Rails content management system.\n\n= CHANGELOG:\n\nChanges to note in this version (from COOK-117).\n\n* added gem install capabilities\n* default to gem install\n* removed dependency on ezmobius\/chef-deploy (use deploy provider instead)\n* fixed tiny error in attributes.\n* updated attribute syntax\n\n= REQUIREMENTS:\n\n== Platform:\n\nTested on Ubuntu 9.04, uses the Opscode Apache2 cookbook which is Ubuntu\/Debian specific.\n\nRequires Chef 0.7.12 for Deploy resource when installing from Radiant's git repo.\n\n== Cookbooks:\n\nOpscode cookbooks (http:\/\/github.com\/opscode\/cookbooks\/tree\/master)\n\n* git\n* sqlite\n* rails\n* apache2\n\n= ATTRIBUTES:\n\n* radiant[:edge] - Do a deploy from github repo if true, use gems if false, default false.\n* radiant[:branch] - Branch to deploy from, default HEAD.\n* radiant[:migrate] - Whether to do a database migration, default false.\n* radiant[:migrate_command] - Command to do a database migration, default 'rake db:migrate'.\n* radiant[:environment] - Rails environment to use, default is production.\n* radiant[:revision] - Revision to deploy, default HEAD.\n* radiant[:action] - Whether to deploy, rollback or nothing, default nothing.\n\n= USAGE:\n\nThis recipe uses SQLite3 for the database by default. To set up the default database to get Radiant rolling, run a db:bootstrap by changing the radiant[:migrate] command to the following in the webui:\n\n yes | rake production db:bootstrap \\\n ADMIN_NAME=Administrator \\\n ADMIN_USERNAME=admin \\\n ADMIN_PASSWORD=radiant \\\n DATABASE_TEMPLATE=empty.yml\n\nChange as required for your environment. If the target system doesn't have \/usr\/bin\/yes, use echo 'yes' instead.\n\nRadiant supports other database backends. We don't yet have automation ready to set up a database user and grant privileges, or creating the database itself.\n\n= LICENSE and AUTHOR:\n\n\nAuthor:: Joshua Timberman ()\nCopyright:: 2009, Opscode, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\n", + "replacing": { + + }, + "dependencies": { + "rails": [ + + ], + "passenger_apache2": [ + + ], + "mysql": [ + + ], + "sqlite": [ + + ], + "git": [ + + ], + "apache2": [ + + ] + } +} \ No newline at end of file diff --git a/radiant/metadata.rb b/radiant/metadata.rb new file mode 100644 index 0000000..fcbd009 --- /dev/null +++ b/radiant/metadata.rb @@ -0,0 +1,44 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs radiant from Git repository" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.9" + +%w{ git sqlite rails apache2 mysql passenger_apache2 apache2 }.each do |cb| + depends cb +end + +%w{ ubuntu debian }.each do |os| + supports os +end + +attribute "radiant/branch", + :display_name => "Radiant Branch", + :description => "Branch from Git to use", + :default => "HEAD" + +attribute "radiant/migrate", + :display_name => "Radiant Migrate", + :description => "Whether to do a migration", + :default => "false" + +attribute "radiant/migrate_command", + :display_name => "Radiant Migrate Command", + :description => "Command to perform migration", + :default => "rake db:migrate" + +attribute "radiant/environment", + :display_name => "Radiant Environment", + :description => "Rails environment to use", + :default => "production" + +attribute "radiant/revision", + :display_name => "Radiant Revision", + :description => "Revision to use from Git", + :default => "HEAD" + +attribute "radiant/action", + :display_name => "Radiant Action", + :description => "Whether to deploy the application or not", + :default => "nothing" diff --git a/radiant/recipes/default.rb b/radiant/recipes/default.rb new file mode 100644 index 0000000..3080367 --- /dev/null +++ b/radiant/recipes/default.rb @@ -0,0 +1,95 @@ +# +# Author:: Joshua Timberman +# Author:: Daniel DeLeo +# Cookbook Name:: radiant +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# Copyright 2009, Daniel DeLeo +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +appname = "radiant" + +include_recipe "git" +include_recipe "sqlite" +include_recipe "rails" +include_recipe "apache2" +include_recipe "apache2::mod_rewrite" +include_recipe "passenger_apache2::mod_rails" + +gem_package "sqlite3-ruby" + +if radiant_edge? + + %w{config log pids sqlite system}.each do |dir| + directory "/srv/#{appname}/shared/#{dir}" do + recursive true + owner "railsdev" + group "railsdev" + mode "0775" + end + end + + template "/srv/#{appname}/shared/config/database.yml" do + source "database.yml.erb" + owner "railsdev" + group "railsdev" + variables :appname => appname + mode "0664" + end + + file "/srv/#{appname}/shared/sqlite/production.sqlite3" do + owner "railsdev" + group "railsdev" + mode "0664" + end + + deploy "/srv/#{appname}" do + repo "git://github.com/radiant/radiant.git" + branch node[:radiant][:branch] + user "railsdev" + enable_submodules false + migrate node[:radiant][:migrate] + migration_command node[:radiant][:migrate_command] + environment node[:radiant][:environment] + shallow_clone true + revision node[:radiant][:revision] + action node[:radiant][:action].to_sym + restart_command "touch tmp/restart.txt" + end +else + directory "/srv/#{appname}/current/" do + recursive true + owner "railsdev" + group "railsdev" + mode "0775" + end + + gem_package "radiant" + + execute "radiant_generate" do + command "radiant -d sqlite3 /srv/#{appname}/current/" + creates "/srv/#{appname}/current/public" + user "railsdev" + end +end + +web_app "#{appname}" do + docroot "/srv/#{appname}/current/public" + template "#{appname}.conf.erb" + server_name "#{appname}.#{node[:domain]}" + server_aliases [ "#{appname}", node[:hostname] ] + rails_env "production" +end diff --git a/radiant/templates/default/database.yml.erb b/radiant/templates/default/database.yml.erb new file mode 100644 index 0000000..e9a7e46 --- /dev/null +++ b/radiant/templates/default/database.yml.erb @@ -0,0 +1,4 @@ +production: + adapter: sqlite3 + database: /srv/<%= @appname %>/shared/sqlite/production.sqlite3 + timeout: 5000 diff --git a/radiant/templates/default/radiant.conf.erb b/radiant/templates/default/radiant.conf.erb new file mode 100644 index 0000000..4115cc7 --- /dev/null +++ b/radiant/templates/default/radiant.conf.erb @@ -0,0 +1,21 @@ + + ServerName <%= @params[:server_name] %> + ServerAlias <% @params[:server_aliases].each do |a| %><%= "#{a}" %> <% end %> + DocumentRoot <%= @params[:docroot] %> + + RailsBaseURI / + RailsEnv <%= @params[:rails_env] %> + + PassengerMaxPoolSize <%= @node[:rails][:max_pool_size] %> + + > + Options FollowSymLinks + AllowOverride None + Order allow,deny + Allow from all + + + LogLevel info + ErrorLog <%= @node[:apache][:log_dir] %>/<%= @params[:name] %>-error.log + CustomLog <%= @node[:apache][:log_dir] %>/<%= @params[:name] %>-access.log combined + diff --git a/rails/README.rdoc b/rails/README.rdoc new file mode 100644 index 0000000..70e8583 --- /dev/null +++ b/rails/README.rdoc @@ -0,0 +1,54 @@ += DESCRIPTION: + +Installs Rails and contains Rails tuning parameters. + += REQUIREMENTS: + +== Platform: + +Tested on Ubuntu 8.10, should work on other platforms. + +== Cookbooks: + +Opscode cookbooks, http://github.com/opscode/cookbooks/tree/master: + +* ruby +* apache2 +* passenger + += ATTRIBUTES: + +* rails[:version] - Install the specified version. Default false (installs latest). +* rails[:environment] - Set Rails environment. Default production. + += USAGE: + +The recommended Rails application deployment method is Passenger and use the Apache2 cookbook's web_app define. + + include_recipe "apache2" + include_recipe "passenger" + include_recipe "rails" + + web_app "some_rails_app" do + docroot "/srv/some_rails_app/public" + template "some_rails_app.conf.erb" + end + +We provide an example rails application vhost config file in this cookbook. Remember, for Passenger, DocumentRoot (docroot) needs 'public'. Per the web_app define, other parameters can be passed arbitrarily and used in the template. + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/rails/attributes/rails.rb b/rails/attributes/rails.rb new file mode 100644 index 0000000..f3122b7 --- /dev/null +++ b/rails/attributes/rails.rb @@ -0,0 +1,3 @@ +set_unless[:rails][:version] = false +set_unless[:rails][:environment] = "production" +set_unless[:rails][:max_pool_size] = 4 diff --git a/rails/metadata.json b/rails/metadata.json new file mode 100644 index 0000000..ae78d6c --- /dev/null +++ b/rails/metadata.json @@ -0,0 +1,99 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs rails and provides a sample template for use with passenger", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "rails": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "fedora": [ + + ], + "centos": [ + + ], + "redhat": [ + + ], + "debian": [ + + ] + }, + "version": "0.8.0", + "name": "rails", + "conflicting": { + + }, + "attributes": { + "rails": { + "type": "hash", + "multiple_values": false, + "description": "Hash of Rails attributes", + "display_name": "Rails", + "recipes": [ + + ], + "required": false + }, + "rails\/version": { + "default": "false", + "type": "string", + "multiple_values": false, + "description": "Specify the version of Rails to install", + "display_name": "Rails Version", + "recipes": [ + + ], + "required": false + }, + "rails\/max_pool_size": { + "default": "4", + "type": "string", + "multiple_values": false, + "description": "Specify the MaxPoolSize in the Apache vhost", + "display_name": "Rails Max Pool Size", + "recipes": [ + + ], + "required": false + }, + "rails\/environment": { + "default": "production", + "type": "string", + "multiple_values": false, + "description": "Specify the environment to use for Rails", + "display_name": "Rails Environment", + "recipes": [ + + ], + "required": false + } + }, + "providing": { + "rails": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nInstalls Rails and contains Rails tuning parameters.\n\n= REQUIREMENTS:\n\n== Platform:\n\nTested on Ubuntu 8.10, should work on other platforms.\n\n== Cookbooks:\n\nOpscode cookbooks, http:\/\/github.com\/opscode\/cookbooks\/tree\/master:\n\n* ruby \n* apache2 \n* passenger \n\n= ATTRIBUTES: \n\n* rails[:version] - Install the specified version. Default false (installs latest).\n* rails[:environment] - Set Rails environment. Default production.\n\n= USAGE:\n\nThe recommended Rails application deployment method is Passenger and use the Apache2 cookbook's web_app define.\n\n include_recipe \"apache2\"\n include_recipe \"passenger\"\n include_recipe \"rails\"\n\n web_app \"some_rails_app\" do\n docroot \"\/srv\/some_rails_app\/public\"\n template \"some_rails_app.conf.erb\"\n end\n\nWe provide an example rails application vhost config file in this cookbook. Remember, for Passenger, DocumentRoot (docroot) needs 'public'. Per the web_app define, other parameters can be passed arbitrarily and used in the template. \n\n= LICENSE and AUTHOR:\n\nAuthor:: Joshua Timberman ()\nCopyright:: 2009, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "replacing": { + + }, + "dependencies": { + "apache2": [ + + ], + "ruby": [ + + ] + } +} \ No newline at end of file diff --git a/rails/metadata.rb b/rails/metadata.rb new file mode 100644 index 0000000..75112e4 --- /dev/null +++ b/rails/metadata.rb @@ -0,0 +1,35 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs rails and provides a sample template for use with passenger" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.8" + +%w{ ruby apache2 }.each do |cb| + depends cb +end + +%w{ ubuntu debian centos redhat fedora}.each do |os| + supports os +end + +attribute "rails", + :display_name => "Rails", + :description => "Hash of Rails attributes", + :type => "hash" + +attribute "rails/version", + :display_name => "Rails Version", + :description => "Specify the version of Rails to install", + :default => "false" + +attribute "rails/environment", + :display_name => "Rails Environment", + :description => "Specify the environment to use for Rails", + :default => "production" + +attribute "rails/max_pool_size", + :display_name => "Rails Max Pool Size", + :description => "Specify the MaxPoolSize in the Apache vhost", + :default => "4" + diff --git a/rails/recipes/default.rb b/rails/recipes/default.rb new file mode 100644 index 0000000..fcef404 --- /dev/null +++ b/rails/recipes/default.rb @@ -0,0 +1,31 @@ +# +# Cookbook Name:: rails +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "ruby" + +%w{ rails actionmailer actionpack activerecord activesupport activeresource }.each do |rails_gem| + gem_package rails_gem do + if node[:rails][:version] + version node[:rails][:version] + action :install + else + action :install + end + end +end diff --git a/rails/templates/default/rails_app.conf.erb b/rails/templates/default/rails_app.conf.erb new file mode 100644 index 0000000..7081289 --- /dev/null +++ b/rails/templates/default/rails_app.conf.erb @@ -0,0 +1,30 @@ + + ServerName <%= @params[:server_name] %> + DocumentRoot <%= @params[:docroot] %> + + RailsBaseURI / + RailsMaxPoolSize <%= @node[:rails][:max_pool_size] %> + RailsPoolIdleTime 3600 + RailsEnv '<%= @node[:rails][:environment] %>' + + LogLevel info + ErrorLog <%= @node[:apache][:log_dir] %>/<%= @params[:name] %>_error.log + CustomLog <%= @node[:apache][:log_dir] %>/<%= @params[:name] %>_access.log combined + + ErrorDocument 404 /404.html + ErrorDocument 500 /500.html + + RewriteEngine On + + # Handle maintenance mode + RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f + RewriteCond %{SCRIPT_FILENAME} !maintenance.html + RewriteRule ^/(.*)$ /system/maintenance.html [L] + + > + Options FollowSymLinks + AllowOverride None + Order allow,deny + Allow from all + + diff --git a/rails_enterprise/README.rdoc b/rails_enterprise/README.rdoc new file mode 100644 index 0000000..f4c1f87 --- /dev/null +++ b/rails_enterprise/README.rdoc @@ -0,0 +1,24 @@ += DESCRIPTION: + +Installs Ruby on Rails under Ruby Enterprise Edition + += REQUIREMENTS: + +ruby_enterprise + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/rails_enterprise/metadata.json b/rails_enterprise/metadata.json new file mode 100644 index 0000000..4d6966a --- /dev/null +++ b/rails_enterprise/metadata.json @@ -0,0 +1,45 @@ +{ + "replacing": { + + }, + "dependencies": { + "ruby_enterprise": [ + + ] + }, + "groupings": { + + }, + "long_description": "= DESCRIPTION:\n\nInstalls Ruby on Rails under Ruby Enterprise Edition\n\n= REQUIREMENTS:\n\nruby_enterprise\n\n= LICENSE and AUTHOR:\n\nAuthor:: Joshua Timberman ()\nCopyright:: 2009, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "description": "Installs Ruby on Rails with Ruby Enterprise Edition", + "recommendations": { + + }, + "platforms": { + "ubuntu": [ + + ] + }, + "version": "0.1.0", + "maintainer": "Opscode, Inc.", + "name": "rails_enterprise", + "suggestions": { + + }, + "maintainer_email": "ops@opscode.com", + "recipes": { + "rails_enterprise": "" + }, + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "rails_enterprise": [ + + ] + }, + "license": "Apache 2.0" +} \ No newline at end of file diff --git a/rails_enterprise/metadata.rb b/rails_enterprise/metadata.rb new file mode 100644 index 0000000..818c83f --- /dev/null +++ b/rails_enterprise/metadata.rb @@ -0,0 +1,12 @@ +maintainer "Opscode, Inc." +maintainer_email "ops@opscode.com" +license "Apache 2.0" +description "Installs Ruby on Rails with Ruby Enterprise Edition" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.1" + +%w{ ruby_enterprise }.each do |cb| + depends cb +end + +supports "ubuntu" diff --git a/rails_enterprise/recipes/default.rb b/rails_enterprise/recipes/default.rb new file mode 100644 index 0000000..497232a --- /dev/null +++ b/rails_enterprise/recipes/default.rb @@ -0,0 +1,29 @@ +# +# Cookbook Name:: rails_enterprise +# Recipe:: default +# +# Author:: Joshua Timberman () +# +# Copyright 2009-2010, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include_recipe "ruby_enterprise" + +ree_gem "rake" do + version "0.8.7" +end + +ree_gem "rails" do + version "2.3.5" +end diff --git a/redmine/README.rdoc b/redmine/README.rdoc new file mode 100644 index 0000000..a6a9202 --- /dev/null +++ b/redmine/README.rdoc @@ -0,0 +1,58 @@ += DESCRIPTION: + +Installs Redmine, a Ruby on Rails ticket tracking and wiki tool. + += REQUIREMENTS: + +== Platform: + +Tested on Ubuntu 9.04, uses the Opscode Apache2 cookbook which is Ubuntu/Debian specific. + +== Cookbooks: + +Opscode cookbooks (http://github.com/opscode/cookbooks/tree/master) + +* git +* sqlite +* mysql +* rails +* passenger_apache2 +* apache2 + += ATTRIBUTES: + +* redmine[:dl_id] - download id on the rubyforge page, needs to be updated on new redmine releases. +* redmine[:version] - release version of redmine to use. +* redmine[:dir] - directory where redmine will be installed. +* redmine[:db][:type] - type of database to use, default is sqlite. mysql or postgresql can be used, but the recipe will need to modified, and the next three attributes adjusted. +* redmine[:db][:user] - database user to connect as, default is redmine. +* redmine[:db][:password] - password for the user, default is a random string generated with OpenSSL::Random.random_bytes. +* redmine[:db][:hostname] - hostname of database server, default is localhost. + += USAGE: + +Including this recipe in a run_list, role or on a node will install Redmine as a Passenger application under Apache2. + + include_recipe "redmine" + +You'll probably want to customize it to fit your environment, as a site-cookbook, especially if you want to use something besides sqlite as the database backend. The attributes file has database_master commented out as an example start on using a node search to determine a database host. See the Chef wiki regarding searches for more information. + + http://wiki.opscode.com/display/chef/Search+Indexes + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/redmine/attributes/redmine.rb b/redmine/attributes/redmine.rb new file mode 100644 index 0000000..7c4b773 --- /dev/null +++ b/redmine/attributes/redmine.rb @@ -0,0 +1,36 @@ +# Cookbook Name:: redmine +# Attributes:: redmine +# +# Copyright 2009, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'openssl' + +pw = String.new + +while pw.length < 20 + pw << OpenSSL::Random.random_bytes(1).gsub(/\W/, '') +end + +#database_server = search(:node, "database_master:true").map {|n| n['fqdn']}.first + +set[:redmine][:dir] = "/srv/redmine-#{redmine[:version]}" + +set_unless[:redmine][:dl_id] = "56909" +set_unless[:redmine][:version] = "0.8.4" + +set_unless[:redmine][:db][:type] = "sqlite" +set_unless[:redmine][:db][:user] = "redmine" +set_unless[:redmine][:db][:password] = pw +set_unless[:redmine][:db][:hostname] = "localhost" diff --git a/redmine/metadata.json b/redmine/metadata.json new file mode 100644 index 0000000..75f8908 --- /dev/null +++ b/redmine/metadata.json @@ -0,0 +1,57 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs and configures redmine as a Rails app in passenger+apache2", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "redmine": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.8.0", + "name": "redmine", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "redmine": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + "rails": [ + + ], + "mysql": [ + + ], + "passenger_apache2": [ + + ], + "sqlite": [ + + ], + "apache2": [ + + ] + } +} \ No newline at end of file diff --git a/redmine/metadata.rb b/redmine/metadata.rb new file mode 100644 index 0000000..e667d0e --- /dev/null +++ b/redmine/metadata.rb @@ -0,0 +1,13 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures redmine as a Rails app in passenger+apache2" +version "0.9" + +%w{ apache2 rails passenger_apache2 mysql sqlite }.each do |cb| + depends cb +end + +%w{ ubuntu debian }.each do |os| + supports os +end diff --git a/redmine/recipes/default.rb b/redmine/recipes/default.rb new file mode 100644 index 0000000..de27b5d --- /dev/null +++ b/redmine/recipes/default.rb @@ -0,0 +1,74 @@ +# +# Author: Joshua Timberman +# Cookbook Name:: redmine +# Recipe:: default +# +# Copyright 2008-2009, Joshua Timberman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "rails" +include_recipe "apache2" +include_recipe "apache2::mod_rewrite" +include_recipe "passenger_apache2::mod_rails" + +bash "install_redmine" do + cwd "/srv" + user "root" + code <<-EOH + wget http://rubyforge.org/frs/download.php/#{node[:redmine][:dl_id]}/redmine-#{node[:redmine][:version]}.tar.gz + tar xf redmine-#{node[:redmine][:version]}.tar.gz + chown -R #{node[:apache][:user]} redmine-#{node[:redmine][:version]} + EOH + not_if { File.exists?("/srv/redmine-#{node[:redmine][:version]}/Rakefile") } +end + +link "/srv/redmine" do + to "/srv/redmine-#{node[:redmine][:version]}" +end + +case node[:redmine][:db][:type] +when "sqlite" + include_recipe "sqlite" + gem_package "sqlite3-ruby" + file "/srv/redmine-#{node[:redmine][:version]}/db/production.db" do + owner node[:apache][:user] + group node[:apache][:user] + mode "0644" + end +when "mysql" + include_recipe "mysql::client" +end + +template "/srv/redmine-#{node[:redmine][:version]}/config/database.yml" do + source "database.yml.erb" + owner "root" + group "root" + variables :database_server => node[:redmine][:db][:hostname] + mode "0664" +end + +execute "rake db:migrate RAILS_ENV='production'" do + user node[:apache][:user] + cwd "/srv/redmine-#{node[:redmine][:version]}" + not_if { File.exists?("/srv/redmine-#{node[:redmine][:version]}/db/schema.rb") } +end + +web_app "redmine" do + docroot "/srv/redmine/public" + template "redmine.conf.erb" + server_name "redmine.#{node[:domain]}" + server_aliases [ "redmine", node[:hostname] ] + rails_env "production" +end diff --git a/redmine/templates/default/database.yml.erb b/redmine/templates/default/database.yml.erb new file mode 100644 index 0000000..92cb2cd --- /dev/null +++ b/redmine/templates/default/database.yml.erb @@ -0,0 +1,22 @@ +# MySQL (default setup). Versions 4.1 and 5.0 are recommended. +# +# Get the fast C bindings: +# gem install mysql +# (on OS X: gem install mysql -- --include=/usr/local/lib) +# And be sure to use new-style password hashing: +# http://dev.mysql.com/doc/refman/5.0/en/old-client.html + +<% case @node[:redmine][:db][:type] -%> +<% when "mysql" -%> +production: + adapter: mysql + database: redmine + host: <%= @database_server %> + username: <%= @node[:redmine][:db][:user] %> + password: <%= @node[:redmine][:db][:password] %> + encoding: utf8 +<% when "sqlite" -%> +production: + adapter: sqlite3 + dbfile: db/production.db +<% end -%> diff --git a/redmine/templates/default/port_redmine.erb b/redmine/templates/default/port_redmine.erb new file mode 100644 index 0000000..609c59b --- /dev/null +++ b/redmine/templates/default/port_redmine.erb @@ -0,0 +1,2 @@ +# Redmine +-A FWR -p tcp -m tcp --dport 3000 -j ACCEPT \ No newline at end of file diff --git a/redmine/templates/default/redmine.conf.erb b/redmine/templates/default/redmine.conf.erb new file mode 100644 index 0000000..93ca5a4 --- /dev/null +++ b/redmine/templates/default/redmine.conf.erb @@ -0,0 +1,19 @@ + + ServerName <%= @params[:server_name] %> + ServerAlias <% @params[:server_aliases].each do |a| %><%= "#{a}" %> <% end %> + DocumentRoot <%= @params[:docroot] %> + + RailsBaseURI / + RailsEnv <%= @params[:rails_env] %> + + > + Options FollowSymLinks + AllowOverride None + Order allow,deny + Allow from all + + + LogLevel info + ErrorLog <%= @node[:apache][:log_dir] %>/<%= @params[:name] %>-error.log + CustomLog <%= @node[:apache][:log_dir] %>/<%= @params[:name] %>-access.log combined + diff --git a/redmine/templates/default/settings.yml.erb b/redmine/templates/default/settings.yml.erb new file mode 100644 index 0000000..5006445 --- /dev/null +++ b/redmine/templates/default/settings.yml.erb @@ -0,0 +1,142 @@ +# redMine - project management software +# Copyright (C) 2006-2007 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +# DO NOT MODIFY THIS FILE !!! +# Settings can be defined through the application in Admin -> Settings + +app_title: + default: Redmine +app_subtitle: + default: Project management +welcome_text: + default: +login_required: + default: 0 +self_registration: + default: '2' +lost_password: + default: 1 +attachment_max_size: + format: int + default: 5120 +issues_export_limit: + format: int + default: 500 +activity_days_default: + format: int + default: 30 +per_page_options: + default: '25,50,100' +mail_from: + default: redmine@example.net +bcc_recipients: + default: 1 +plain_text_mail: + default: 0 +text_formatting: + default: textile +wiki_compression: + default: "" +default_language: + default: en +host_name: + default: localhost:3000 +protocol: + default: http +feeds_limit: + format: int + default: 15 +diff_max_lines_displayed: + format: int + default: 1500 +enabled_scm: + serialized: true + default: + - Subversion + - Darcs + - Mercurial + - Cvs + - Bazaar + - Git +autofetch_changesets: + default: 1 +sys_api_enabled: + default: 0 +commit_ref_keywords: + default: 'refs,references,IssueID' +commit_fix_keywords: + default: 'fixes,closes' +commit_fix_status_id: + format: int + default: 0 +commit_fix_done_ratio: + default: 100 +# autologin duration in days +# 0 means autologin is disabled +autologin: + format: int + default: 0 +# date format +date_format: + default: '' +time_format: + default: '' +user_format: + default: :firstname_lastname + format: symbol +cross_project_issue_relations: + default: 0 +notified_events: + serialized: true + default: + - issue_added + - issue_updated +mail_handler_api_enabled: + default: 0 +mail_handler_api_key: + default: +issue_list_default_columns: + serialized: true + default: + - tracker + - status + - priority + - subject + - assigned_to + - updated_on +display_subprojects_issues: + default: 1 +default_projects_public: + default: 1 +sequential_project_identifiers: + default: 0 +# encodings used to convert repository files content to UTF-8 +# multiple values accepted, comma separated +repositories_encodings: + default: '' +# encoding used to convert commit logs to UTF-8 +commit_logs_encoding: + default: 'UTF-8' +ui_theme: + default: '' +emails_footer: + default: |- + You have received this notification because you have either subscribed to it, or are involved in it. + To change your notification preferences, please click here: http://hostname/my/account +gravatar_enabled: + default: 0 diff --git a/redmine/templates/default/sv-redmine-log-run.erb b/redmine/templates/default/sv-redmine-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/redmine/templates/default/sv-redmine-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/redmine/templates/default/sv-redmine-run.erb b/redmine/templates/default/sv-redmine-run.erb new file mode 100644 index 0000000..cab6f5c --- /dev/null +++ b/redmine/templates/default/sv-redmine-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec chpst -u <%= @node[:apache_user] %> /srv/redmine/script/server -e production diff --git a/resolver/attributes/resolver.rb b/resolver/attributes/resolver.rb new file mode 100644 index 0000000..86dbae4 --- /dev/null +++ b/resolver/attributes/resolver.rb @@ -0,0 +1,2 @@ +set_unless[:resolver][:search] = domain +set_unless[:resolver][:nameservers] = [ "" ] diff --git a/resolver/metadata.json b/resolver/metadata.json new file mode 100644 index 0000000..a6daae4 --- /dev/null +++ b/resolver/metadata.json @@ -0,0 +1,94 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Configures \/etc\/resolv.conf", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "resolver": "" + }, + "suggestions": { + + }, + "platforms": { + "freebsd": [ + + ], + "ubuntu": [ + + ], + "openbsd": [ + + ], + "fedora": [ + + ], + "macosx": [ + + ], + "centos": [ + + ], + "redhat": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "resolver", + "conflicting": { + + }, + "attributes": { + "resolver\/nameservers": { + "default": [ + "" + ], + "type": "array", + "multiple_values": false, + "description": "Default nameservers", + "display_name": "Resolver Nameservers", + "recipes": [ + + ], + "required": false + }, + "resolver": { + "type": "hash", + "multiple_values": false, + "description": "Hash of Resolver attributes", + "display_name": "Resolver", + "recipes": [ + + ], + "required": false + }, + "resolver\/search": { + "default": "domain", + "type": "string", + "multiple_values": false, + "description": "Default domain to search", + "display_name": "Resolver Search", + "recipes": [ + + ], + "required": false + } + }, + "providing": { + "resolver": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/resolver/metadata.rb b/resolver/metadata.rb new file mode 100644 index 0000000..02fa146 --- /dev/null +++ b/resolver/metadata.rb @@ -0,0 +1,26 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Configures /etc/resolv.conf" +version "0.7" + +%w{ ubuntu debian fedora centos redhat freebsd openbsd macosx }.each do |os| + supports os +end + +attribute "resolver", + :display_name => "Resolver", + :description => "Hash of Resolver attributes", + :type => "hash" + +attribute "resolver/search", + :display_name => "Resolver Search", + :description => "Default domain to search", + :default => "domain" + +attribute "resolver/nameservers", + :display_name => "Resolver Nameservers", + :description => "Default nameservers", + :type => "array", + :default => [""] + diff --git a/resolver/recipes/default.rb b/resolver/recipes/default.rb new file mode 100644 index 0000000..7dd3932 --- /dev/null +++ b/resolver/recipes/default.rb @@ -0,0 +1,27 @@ +# +# Cookbook Name:: resolver +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# = Requires +# * node[:resolver][:nameservers] + +template "/etc/resolv.conf" do + source "resolv.conf.erb" + owner "root" + group "root" + mode 0644 +end diff --git a/resolver/templates/default/resolv.conf.erb b/resolver/templates/default/resolv.conf.erb new file mode 100644 index 0000000..35b536e --- /dev/null +++ b/resolver/templates/default/resolv.conf.erb @@ -0,0 +1,4 @@ +search <%= @node[:resolver][:search] %> +<% @node[:resolver][:nameservers].each do |nameserver| -%> +nameserver <%= nameserver %> +<% end -%> diff --git a/rsnapshot/attributes/rsnapshot.rb b/rsnapshot/attributes/rsnapshot.rb new file mode 100755 index 0000000..33c1b61 --- /dev/null +++ b/rsnapshot/attributes/rsnapshot.rb @@ -0,0 +1 @@ +rsnapshot Mash.new unless attribute?(:rsnapshot) diff --git a/rsnapshot/recipes/client.rb b/rsnapshot/recipes/client.rb new file mode 100755 index 0000000..a92fb25 --- /dev/null +++ b/rsnapshot/recipes/client.rb @@ -0,0 +1,2 @@ +package "rsnapshot" + diff --git a/rsnapshot/recipes/server.rb b/rsnapshot/recipes/server.rb new file mode 100755 index 0000000..6edce64 --- /dev/null +++ b/rsnapshot/recipes/server.rb @@ -0,0 +1,5 @@ +package "rsnapshot" + +template "/etc/rsnapshot.conf" do + source "rsnapshot.conf.erb" +end \ No newline at end of file diff --git a/rsnapshot/templates/default/rsnapshot.conf.erb b/rsnapshot/templates/default/rsnapshot.conf.erb new file mode 100755 index 0000000..892b675 --- /dev/null +++ b/rsnapshot/templates/default/rsnapshot.conf.erb @@ -0,0 +1,54 @@ +# Automatically generated by Chef. Local changes will be overwritten. +# +# This file requires tabs between elements +# +# Directories require a trailing slash: +# right: /home/ +# wrong: /home + +config_version 1.2 + +snapshot_root /u/backup/snapshots + +cmd_cp /bin/cp +cmd_rm /bin/rm +cmd_rsync /usr/bin/rsync +cmd_ssh /usr/bin/ssh +cmd_logger /usr/bin/logger + +######################################### +# BACKUP INTERVALS # +# Must be unique and in ascending order # +# i.e. hourly, daily, weekly, etc. # +######################################### + +interval hourly 6 +interval daily 7 +interval weekly 4 +#interval monthly 3 + +# Verbose level, 1 through 5. +# 1 Quiet Print fatal errors only +# 2 Default Print errors and warnings only +# 3 Verbose Show equivalent shell commands being executed +# 4 Extra Verbose Show extra verbose information +# 5 Debug mode Everything +# +verbose 2 + +# Same as "verbose" above, but controls the amount of data sent to the +# logfile, if one is being used. The default is 3. +# +loglevel 3 + +lockfile /var/run/rsnapshot.pid + +#rsync_short_args -a +#rsync_long_args --delete --numeric-ids --relative --delete-excluded + +# LOCALHOST +<% if @node[:rsnapshot][:backups] %> +<% @node[:rsnapshot][:backups].each do |source, config| %> +backup <%= source %> <%= config[:target] %> +<% end %> +<% end %> \ No newline at end of file diff --git a/rsync/metadata.json b/rsync/metadata.json new file mode 100644 index 0000000..208a073 --- /dev/null +++ b/rsync/metadata.json @@ -0,0 +1,52 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs rsync", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "rsync": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "fedora": [ + + ], + "centos": [ + + ], + "debian": [ + + ], + "redhat": [ + + ] + }, + "version": "0.7.0", + "name": "rsync", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "rsync": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/rsync/metadata.rb b/rsync/metadata.rb new file mode 100644 index 0000000..9d2c3f1 --- /dev/null +++ b/rsync/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs rsync" +version "0.7" + +%w{ centos fedora redhat ubuntu debian }.each do |os| + supports os +end diff --git a/rsync/recipes/default.rb b/rsync/recipes/default.rb new file mode 100644 index 0000000..396a9ec --- /dev/null +++ b/rsync/recipes/default.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: rsync +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "rsync" diff --git a/rsyslog/README.rdoc b/rsyslog/README.rdoc new file mode 100644 index 0000000..ed8ee93 --- /dev/null +++ b/rsyslog/README.rdoc @@ -0,0 +1,55 @@ += DESCRIPTION: + +Installs rsyslog to replace sysklogd for client and/or server use. By default, server will be set up to log to files. + += REQUIREMENTS: + +== Platform: + +Tested on Ubuntu 8.10. + +== Cookbooks: + += ATTRIBUTES: + +* rsyslog[:log_dir] - specify the directory to store logs (applicable to server only) +* rsyslog[:server] - specify the remote rsyslog server. +* rsyslog[:protocol] - specify whether to use udp or tcp for remote log transmission. tcp is default. + += USAGE: + +To replace the sysklogd syslog service with rsyslog: + + include_recipe "rsyslog" + +To set up a client with a remote [r]syslog server: + + include_recipe "rsyslog::client" + +By default, this cookbook will use TCP so the server should be configured for TCP. This can be done easily with the server recipe: + + include_recipe "rsyslog::server" + +To switch to UDP, change the rsyslog[:protocol] attribute. Note this needs to be done on each client as well. + +Also, the server configuration will set up log_dir for each client, by date. Structure: + + <%= @log_dir %>/YEAR/MONTH/DAY/HOSTNAME/"logfile" + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () + +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/rsyslog/attributes/rsyslog.rb b/rsyslog/attributes/rsyslog.rb new file mode 100644 index 0000000..c79897c --- /dev/null +++ b/rsyslog/attributes/rsyslog.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: rsyslog +# Attributes:: rsyslog +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set_unless[:rsyslog][:log_dir] = "/srv/rsyslog" +set_unless[:rsyslog][:server] = false +set_unless[:rsyslog][:protocol] = "tcp" diff --git a/rsyslog/files/default/rsyslog.default b/rsyslog/files/default/rsyslog.default new file mode 100644 index 0000000..87449b3 --- /dev/null +++ b/rsyslog/files/default/rsyslog.default @@ -0,0 +1,9 @@ +# Generated by Chef +# +# Use v3 native mode, rather than compatibility mode by specifying -c3 +# here. Compatibility mode for older versions is not recommended as +# custom configuration may get messy. +# +# See rsyslogd(8) for more details + +RSYSLOGD_OPTIONS="-c3" diff --git a/rsyslog/files/ubuntu-9.10/rsyslog.default b/rsyslog/files/ubuntu-9.10/rsyslog.default new file mode 100644 index 0000000..2e4599f --- /dev/null +++ b/rsyslog/files/ubuntu-9.10/rsyslog.default @@ -0,0 +1,9 @@ +# Generated by Chef +# +# Options for rsyslogd +# -m 0 disables 'MARK' messages (deprecated, only used in compat mode < 3) +# -r enables logging from remote machines (deprecated, only used in compat mode < 3) +# -x disables DNS lookups on messages received with -r +# -c compatibility mode +# See rsyslogd(8) for more details +RSYSLOGD_OPTIONS="-c4" diff --git a/rsyslog/metadata.json b/rsyslog/metadata.json new file mode 100644 index 0000000..ddbc671 --- /dev/null +++ b/rsyslog/metadata.json @@ -0,0 +1,93 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs and configures rsyslog", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "rsyslog::server": "Sets up an rsyslog server", + "rsyslog::client": "Sets up a client to log to a remote rsyslog server", + "rsyslog": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + ">= 8.10" + ], + "debian": [ + ">= 5.0" + ] + }, + "version": "0.7.0", + "name": "rsyslog", + "conflicting": { + + }, + "attributes": { + "rsyslog\/protocol": { + "default": "tcp", + "type": "string", + "multiple_values": false, + "description": "Set which network protocol to use for rsyslog", + "display_name": "Rsyslog Protocol", + "recipes": [ + + ], + "required": false + }, + "rsyslog": { + "type": "hash", + "multiple_values": false, + "description": "Hash of Rsyslog attributes", + "display_name": "Rsyslog", + "recipes": [ + + ], + "required": false + }, + "rsyslog\/log_dir": { + "default": "\/srv\/rsyslog", + "type": "string", + "multiple_values": false, + "description": "Filesystem location of logs from clients", + "display_name": "Rsyslog Log Directory", + "recipes": [ + + ], + "required": false + }, + "rsyslog\/server": { + "default": "false", + "type": "string", + "multiple_values": false, + "description": "Is this node an rsyslog server?", + "display_name": "Rsyslog Server?", + "recipes": [ + + ], + "required": false + } + }, + "providing": { + "rsyslog::server": [ + + ], + "rsyslog": [ + + ], + "rsyslog::client": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nInstalls rsyslog to replace sysklogd for client and\/or server use. By default, server will be set up to log to files.\n\n= REQUIREMENTS:\n\n== Platform:\n\nTested on Ubuntu 8.10.\n\n== Cookbooks:\n\n= ATTRIBUTES: \n\n* rsyslog[:log_dir] - specify the directory to store logs (applicable to server only)\n* rsyslog[:server] - specify the remote rsyslog server.\n* rsyslog[:protocol] - specify whether to use udp or tcp for remote log transmission. tcp is default.\n\n= USAGE:\n\nTo replace the sysklogd syslog service with rsyslog:\n\n include_recipe \"rsyslog\"\n \nTo set up a client with a remote [r]syslog server:\n\n include_recipe \"rsyslog::client\"\n \nBy default, this cookbook will use TCP so the server should be configured for TCP. This can be done easily with the server recipe:\n\n include_recipe \"rsyslog::server\"\n\nTo switch to UDP, change the rsyslog[:protocol] attribute. Note this needs to be done on each client as well.\n\nAlso, the server configuration will set up log_dir for each client, by date. Structure:\n\n <%= @log_dir %>\/YEAR\/MONTH\/DAY\/HOSTNAME\/\"logfile\"\n\n= LICENSE and AUTHOR:\n \nAuthor:: Joshua Timberman ()\n\nCopyright:: 2009, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/rsyslog/metadata.rb b/rsyslog/metadata.rb new file mode 100644 index 0000000..50b4bbd --- /dev/null +++ b/rsyslog/metadata.rb @@ -0,0 +1,32 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures rsyslog" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.7" +recipe "rsyslog::client", "Sets up a client to log to a remote rsyslog server" +recipe "rsyslog::server", "Sets up an rsyslog server" + +supports "ubuntu", ">= 8.10" +supports "debian", ">= 5.0" + +attribute "rsyslog", + :display_name => "Rsyslog", + :description => "Hash of Rsyslog attributes", + :type => "hash" + +attribute "rsyslog/log_dir", + :display_name => "Rsyslog Log Directory", + :description => "Filesystem location of logs from clients", + :default => "/srv/rsyslog" + +attribute "rsyslog/server", + :display_name => "Rsyslog Server?", + :description => "Is this node an rsyslog server?", + :default => "false" + +attribute "rsyslog/protocol", + :display_name => "Rsyslog Protocol", + :description => "Set which network protocol to use for rsyslog", + :default => "tcp" + diff --git a/rsyslog/recipes/client.rb b/rsyslog/recipes/client.rb new file mode 100644 index 0000000..4fbf58b --- /dev/null +++ b/rsyslog/recipes/client.rb @@ -0,0 +1,40 @@ +# +# Cookbook Name:: rsyslog +# Recipe:: client +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "rsyslog" + +rsyslog_server = node[:rsyslog][:server] ? node[:rsyslog][:server] : search(:node, "rsyslog_server:true").map { |n| n["fqdn"] }.first + +unless node[:rsyslog][:server] + template "/etc/rsyslog.d/remote.conf" do + source "remote.conf.erb" + backup false + variables :server => rsyslog_server, :protocol => node[:rsyslog][:protocol] + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "rsyslog"), :delayed + end + + file "/etc/rsyslog.d/server.conf" do + action :delete + notifies :reload, resources(:service => "rsyslog"), :delayed + only_if do File.exists?("/etc/rsyslog.d/server.conf") end + end +end diff --git a/rsyslog/recipes/default.rb b/rsyslog/recipes/default.rb new file mode 100644 index 0000000..cbf9b5b --- /dev/null +++ b/rsyslog/recipes/default.rb @@ -0,0 +1,61 @@ +# +# Cookbook Name:: rsyslog +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "rsyslog" do + action :install +end + +service "rsyslog" do + supports :restart => true, :reload => true + action [:enable, :start] +end + +remote_file "/etc/default/rsyslog" do + source "rsyslog.default" + owner "root" + group "root" + mode 0644 +end + +directory "/etc/rsyslog.d" do + owner "root" + group "root" + mode 0755 +end + +template "/etc/rsyslog.conf" do + source "rsyslog.conf.erb" + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "rsyslog"), :delayed +end + +case node[:platform] +when "ubuntu" + if node[:platform_version] >= "9.10" + template "/etc/rsyslog.d/50-default.conf" do + source "50-default.conf.erb" + backup false + owner "root" + group "root" + mode 0644 + end + end +end diff --git a/rsyslog/recipes/server.rb b/rsyslog/recipes/server.rb new file mode 100644 index 0000000..b03aaa8 --- /dev/null +++ b/rsyslog/recipes/server.rb @@ -0,0 +1,51 @@ +# +# Cookbook Name:: rsyslog +# Recipe:: server +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "rsyslog" + +directory node[:rsyslog][:log_dir] do + owner "root" + group "root" + mode 0755 +end + +template "/etc/rsyslog.d/server.conf" do + source "server.conf.erb" + backup false + variables :log_dir => node[:rsyslog][:log_dir], :protocol => node[:rsyslog][:protocol] + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "rsyslog"), :delayed +end + +file "/etc/rsyslog.d/remote.conf" do + action :delete + backup false + notifies :reload, resources(:service => "rsyslog"), :delayed + only_if do File.exists?("/etc/rsyslog.d/remote.conf") end +end + +template "/etc/cron.d/rsyslog_gz" do + source "rsyslog_gz.erb" + owner "root" + group "root" + mode 0644 + variables :log_dir => node[:rsyslog][:log_dir] +end diff --git a/rsyslog/templates/default/remote.conf.erb b/rsyslog/templates/default/remote.conf.erb new file mode 100644 index 0000000..02068f4 --- /dev/null +++ b/rsyslog/templates/default/remote.conf.erb @@ -0,0 +1,6 @@ +<% case @protocol -%> +<% when "tcp" -%> +*.* @@<%= @server %> +<% when "udp" -%> +*.* @<%= @server %> +<% end -%> diff --git a/rsyslog/templates/default/rsyslog.conf.erb b/rsyslog/templates/default/rsyslog.conf.erb new file mode 100644 index 0000000..4af21e7 --- /dev/null +++ b/rsyslog/templates/default/rsyslog.conf.erb @@ -0,0 +1,115 @@ +# /etc/rsyslog.conf Configuration file for rsyslog v3. +# +# For more information see +# /usr/share/doc/rsyslog-doc/html/rsyslog_conf.html + + +################# +#### MODULES #### +################# + +$ModLoad imuxsock # provides support for local system logging +$ModLoad imklog # provides kernel logging support (previously done by rklogd) +#$ModLoad immark # provides --MARK-- message capability + +# provides UDP syslog reception +#$ModLoad imudp +#$UDPServerRun 514 + +# provides TCP syslog reception +#$ModLoad imtcp +#$InputTCPServerRun 514 + + +########################### +#### GLOBAL DIRECTIVES #### +########################### + +# +# Use default timestamp format. +# To enable high precision timestamps, comment out the following line. +# +$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat + +# +# Set the default permissions for all log files. +# +$FileOwner root +$FileGroup adm +$FileCreateMode 0640 + +# +# Include all config files in /etc/rsyslog.d/ +# +$IncludeConfig /etc/rsyslog.d/*.conf + + +############### +#### RULES #### +############### + +# +# First some standard log files. Log by facility. +# +auth,authpriv.* /var/log/auth.log +*.*;auth,authpriv.none -/var/log/syslog +#cron.* /var/log/cron.log +daemon.* -/var/log/daemon.log +kern.* -/var/log/kern.log +lpr.* -/var/log/lpr.log +mail.* -/var/log/mail.log +user.* -/var/log/user.log + +# +# Logging for the mail system. Split it up so that +# it is easy to write scripts to parse these files. +# +mail.info -/var/log/mail.info +mail.warn -/var/log/mail.warn +mail.err /var/log/mail.err + +# +# Logging for INN news system. +# +news.crit /var/log/news/news.crit +news.err /var/log/news/news.err +news.notice -/var/log/news/news.notice + +# +# Some "catch-all" log files. +# +*.=debug;\ + auth,authpriv.none;\ + news.none;mail.none -/var/log/debug +*.=info;*.=notice;*.=warn;\ + auth,authpriv.none;\ + cron,daemon.none;\ + mail,news.none -/var/log/messages + +# +# Emergencies are sent to everybody logged in. +# +*.emerg * + +# +# I like to have messages displayed on the console, but only on a virtual +# console I usually leave idle. +# +#daemon,mail.*;\ +# news.=crit;news.=err;news.=notice;\ +# *.=debug;*.=info;\ +# *.=notice;*.=warn /dev/tty8 + +# The named pipe /dev/xconsole is for the `xconsole' utility. To use it, +# you must invoke `xconsole' with the `-file' option: +# +# $ xconsole -file /dev/xconsole [...] +# +# NOTE: adjust the list below, or you'll go crazy if you have a reasonably +# busy site.. +# +daemon.*;mail.*;\ + news.err;\ + *.=debug;*.=info;\ + *.=notice;*.=warn |/dev/xconsole + diff --git a/rsyslog/templates/default/rsyslog_gz.erb b/rsyslog/templates/default/rsyslog_gz.erb new file mode 100644 index 0000000..650b8ea --- /dev/null +++ b/rsyslog/templates/default/rsyslog_gz.erb @@ -0,0 +1,2 @@ +# Chef Name: rsyslog_gz +0 4 * * * find <%= @log_dir %>/$(date +\%Y) -type f -mtime +1 -exec gzip -q {} \; diff --git a/rsyslog/templates/default/server.conf.erb b/rsyslog/templates/default/server.conf.erb new file mode 100644 index 0000000..1fef5e0 --- /dev/null +++ b/rsyslog/templates/default/server.conf.erb @@ -0,0 +1,58 @@ +# Generated by Chef. +# Local modifications will be overwritten. +<% case @protocol -%> +<% when "tcp" -%> +$ModLoad imtcp +$InputTCPServerRun 514 +<% when "udp" -%> +$ModLoad imudp +$UDPServerRun 514 +<% end -%> + +$DirGroup root +$DirCreateMode 0755 +$FileGroup root + +$template PerHostAuth,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/auth.log" +$template PerHostCron,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/cron.log" +$template PerHostSyslog,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/syslog" +$template PerHostDaemon,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/daemon.log" +$template PerHostKern,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/kern.log" +$template PerHostLpr,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/lpr.log" +$template PerHostUser,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/user.log" +$template PerHostMail,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/mail.log" +$template PerHostMailInfo,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/mail.info" +$template PerHostMailWarn,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/mail.warn" +$template PerHostMailErr,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/mail.err" +$template PerHostNewsCrit,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/news.crit" +$template PerHostNewsErr,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/news.err" +$template PerHostNewsNotice,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/news.notice" +$template PerHostDebug,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/debug" +$template PerHostMessages,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/messages" + +auth,authpriv.* ?PerHostAuth +*.*;auth,authpriv.none -?PerHostSyslog +cron.* ?PerHostCron +daemon.* -?PerHostDaemon +kern.* -?PerHostKern +lpr.* -?PerHostLpr +mail.* -?PerHostMail +user.* -?PerHostUser + +mail.info -?PerHostMailInfo +mail.warn ?PerHostMailWarn +mail.err ?PerHostMailErr + +news.crit ?PerHostNewsCrit +news.err ?PerHostNewsErr +news.notice -?PerHostNewsNotice + +*.=debug;\ + auth,authpriv.none;\ + news.none;mail.none -?PerHostDebug + +*.=info;*.=notice;*.=warn;\ + auth,authpriv.none;\ + cron,daemon.none;\ + mail,news.none -?PerHostMessages + diff --git a/rsyslog/templates/ubuntu-9.10/50-default.conf.erb b/rsyslog/templates/ubuntu-9.10/50-default.conf.erb new file mode 100644 index 0000000..b45f419 --- /dev/null +++ b/rsyslog/templates/ubuntu-9.10/50-default.conf.erb @@ -0,0 +1,69 @@ +# Default rules for rsyslog. +# +# For more information see rsyslog.conf(5) and /etc/rsyslog.conf + +# +# First some standard log files. Log by facility. +# +auth,authpriv.* /var/log/auth.log +*.*;auth,authpriv.none -/var/log/syslog +#cron.* /var/log/cron.log +daemon.* -/var/log/daemon.log +kern.* -/var/log/kern.log +lpr.* -/var/log/lpr.log +mail.* -/var/log/mail.log +user.* -/var/log/user.log + +# +# Logging for the mail system. Split it up so that +# it is easy to write scripts to parse these files. +# +mail.info -/var/log/mail.info +mail.warn -/var/log/mail.warn +mail.err /var/log/mail.err + +# +# Logging for INN news system. +# +news.crit /var/log/news/news.crit +news.err /var/log/news/news.err +news.notice -/var/log/news/news.notice + +# +# Some "catch-all" log files. +# +*.=debug;\ + auth,authpriv.none;\ + news.none;mail.none -/var/log/debug +*.=info;*.=notice;*.=warn;\ + auth,authpriv.none;\ + cron,daemon.none;\ + mail,news.none -/var/log/messages + +# +# Emergencies are sent to everybody logged in. +# +*.emerg * + +# +# I like to have messages displayed on the console, but only on a virtual +# console I usually leave idle. +# +#daemon,mail.*;\ +# news.=crit;news.=err;news.=notice;\ +# *.=debug;*.=info;\ +# *.=notice;*.=warn /dev/tty8 + +# The named pipe /dev/xconsole is for the `xconsole' utility. To use it, +# you must invoke `xconsole' with the `-file' option: +# +# $ xconsole -file /dev/xconsole [...] +# +# NOTE: adjust the list below, or you'll go crazy if you have a reasonably +# busy site.. +# +daemon.*;mail.*;\ + news.err;\ + *.=debug;*.=info;\ + *.=notice;*.=warn |/dev/xconsole + diff --git a/rsyslog/templates/ubuntu-9.10/remote.conf.erb b/rsyslog/templates/ubuntu-9.10/remote.conf.erb new file mode 100644 index 0000000..02068f4 --- /dev/null +++ b/rsyslog/templates/ubuntu-9.10/remote.conf.erb @@ -0,0 +1,6 @@ +<% case @protocol -%> +<% when "tcp" -%> +*.* @@<%= @server %> +<% when "udp" -%> +*.* @<%= @server %> +<% end -%> diff --git a/rsyslog/templates/ubuntu-9.10/server.conf.erb b/rsyslog/templates/ubuntu-9.10/server.conf.erb new file mode 100644 index 0000000..1fef5e0 --- /dev/null +++ b/rsyslog/templates/ubuntu-9.10/server.conf.erb @@ -0,0 +1,58 @@ +# Generated by Chef. +# Local modifications will be overwritten. +<% case @protocol -%> +<% when "tcp" -%> +$ModLoad imtcp +$InputTCPServerRun 514 +<% when "udp" -%> +$ModLoad imudp +$UDPServerRun 514 +<% end -%> + +$DirGroup root +$DirCreateMode 0755 +$FileGroup root + +$template PerHostAuth,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/auth.log" +$template PerHostCron,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/cron.log" +$template PerHostSyslog,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/syslog" +$template PerHostDaemon,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/daemon.log" +$template PerHostKern,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/kern.log" +$template PerHostLpr,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/lpr.log" +$template PerHostUser,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/user.log" +$template PerHostMail,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/mail.log" +$template PerHostMailInfo,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/mail.info" +$template PerHostMailWarn,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/mail.warn" +$template PerHostMailErr,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/mail.err" +$template PerHostNewsCrit,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/news.crit" +$template PerHostNewsErr,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/news.err" +$template PerHostNewsNotice,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/news.notice" +$template PerHostDebug,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/debug" +$template PerHostMessages,"<%= @log_dir %>/%$YEAR%/%$MONTH%/%$DAY%/%HOSTNAME%/messages" + +auth,authpriv.* ?PerHostAuth +*.*;auth,authpriv.none -?PerHostSyslog +cron.* ?PerHostCron +daemon.* -?PerHostDaemon +kern.* -?PerHostKern +lpr.* -?PerHostLpr +mail.* -?PerHostMail +user.* -?PerHostUser + +mail.info -?PerHostMailInfo +mail.warn ?PerHostMailWarn +mail.err ?PerHostMailErr + +news.crit ?PerHostNewsCrit +news.err ?PerHostNewsErr +news.notice -?PerHostNewsNotice + +*.=debug;\ + auth,authpriv.none;\ + news.none;mail.none -?PerHostDebug + +*.=info;*.=notice;*.=warn;\ + auth,authpriv.none;\ + cron,daemon.none;\ + mail,news.none -?PerHostMessages + diff --git a/ruby-shadow/attributes/ruby-shadow.rb b/ruby-shadow/attributes/ruby-shadow.rb new file mode 100644 index 0000000..ff345b7 --- /dev/null +++ b/ruby-shadow/attributes/ruby-shadow.rb @@ -0,0 +1 @@ +set_unless[:ruby_shadow][:site_ruby] = "/usr/local/lib/ruby/site_ruby/1.8" \ No newline at end of file diff --git a/ruby-shadow/files/default/shadow-1.4.1/HISTORY b/ruby-shadow/files/default/shadow-1.4.1/HISTORY new file mode 100755 index 0000000..cd7d87e --- /dev/null +++ b/ruby-shadow/files/default/shadow-1.4.1/HISTORY @@ -0,0 +1,34 @@ +[1999/08/18] +* version 1.4.1 + - extconf.rb supports glibc2(libc6). + +[1999/03/09] +* version 1.4 + - require ruby-1.3 or later version. + - sShadowPasswd,mShadow,eFileLock was renamed. + - FileLock class is inner class of Shadow Module. + - lock,unlock was changed. + - lock? method was added. + - getspent,fgetspent doesn't raise EOFError + - class hierarchy was changed. + Shadow Module + + Passwd Module + + Entry Structure + + Group Module (not implemented yet) + + Entry Structure (not implemented yet) + + FileLock Class + +[1998/12/17] +* version 1.3 + - require ruby-1.1d0 or later version. + +[1998/10/31] +* version 1.2 + - only some bug fix. + +[1998/08/31] +* version 1.1 + - structure Shadow::ShadowPasswd is added. + +[1998/07/15] +* version 1.0 released. diff --git a/ruby-shadow/files/default/shadow-1.4.1/MANIFEST b/ruby-shadow/files/default/shadow-1.4.1/MANIFEST new file mode 100755 index 0000000..4d28f0b --- /dev/null +++ b/ruby-shadow/files/default/shadow-1.4.1/MANIFEST @@ -0,0 +1,7 @@ +HISTORY +MANIFEST +README +README.euc +depend +extconf.rb +shadow.c diff --git a/ruby-shadow/files/default/shadow-1.4.1/README b/ruby-shadow/files/default/shadow-1.4.1/README new file mode 100755 index 0000000..f6a0d32 --- /dev/null +++ b/ruby-shadow/files/default/shadow-1.4.1/README @@ -0,0 +1,79 @@ +Shadow Password module + +Copyright (C) 1998-1999 Takaaki Tateishi +Modified at: <1999/8/19 06:47:14 by ttate> +License: Free for any use with your own risk! + + +1. What's this + +This is the module which used when you access +linux shadow password files. + + +2. install + +ruby extconf.rb +make +(make install) + +* Note: + version 1.3 require the ruby-1.3 or later version. + +3. Shadow::Passwd module's methods + +getspent +getspnam(name) +setspent +endspent +fgetspent(file) +sgetspent(str) +putspent(entry,file) +lckpwdf,lock +ulckpwdf,unlock +lock? + +4. Structure + +Shadow::Passwd::Entry (Struct::PasswdEntry) + sp_namp - pointer to null-terminated user name. + sp_pwdp - pointer to null-terminated password. + sp_lstchg - days since Jan 1, 1970 password was last + changed. + sp_min - days before which password may not be changed. + sp_max - days after which password must be changed. + sp_warn - days before password is to expire that user is + warned of pending password expiration. + sp_inact - days after password expires that account is + considered inactive and disabled. + sp_expire - days since Jan 1, 1970 when account will be + + +5. Description + +getspent, getspname, fgetspent and sgetspent each return +a structure Shadow::Passwd::Entry. getspent returns the +next entry from the file, and fgetspent returns the next +entry from the given stream. sgetspent returns a structure +Shadow::Passwd::Entry using the provided string as input. +getspnam searches from the current position in the file for +an entry matching name. +if you get EOF from each operation, you will get nil. + +setspent and endspent may be used to begin and end, respe- +ctively, access to the shadow password file. + +lckpwdf(lock) and ulckpwdf(unlock) methods should be used +to insure exclusive access to the /etc/shadow file. +when either method fail, Exception Shadow::FileLock is raised. +if you use lock as the iterator, unlock is automatically called +when you exit the iterator block. + +6. Reference + +* man shadow +* /usr/include/shadow.h + + + + ttate@jaist.ac.jp diff --git a/ruby-shadow/files/default/shadow-1.4.1/README.euc b/ruby-shadow/files/default/shadow-1.4.1/README.euc new file mode 100755 index 0000000..075a74b --- /dev/null +++ b/ruby-shadow/files/default/shadow-1.4.1/README.euc @@ -0,0 +1,80 @@ +Shadow Password module + +Copyright (C) 1998-1999 Takaaki Tateishi +Modified at: <1999/8/19 06:48:01 by ttate> +License: Free for any use with your own risk! + + +1. ³µÍ× + +linux¤Ë¤ª¤¤¤Æshadow password¥Õ¥¡¥¤¥ë¤ò°·¤¦¤¿¤á +¤Î¥â¥¸¥å¡¼¥ë¡£ + + +2. ¥¤¥ó¥¹¥È¡¼¥ë + +ruby extconf.rb +make +make install + +* Ãí°Õ + shadow-1.3¤Ç¤Ïruby-1.3¤â¤·¤¯¤Ï¤½¤ì°Ê¹ß¤Î¥Ð¡¼¥¸¥ç¥ó + ¤¬É¬ÍפǤ¹¡£ + +3. Shadow::Passwd¥â¥¸¥å¡¼¥ë¤Î¥á¥½¥Ã¥Éã + +getspent +getspnam(name) +setspent +endspent +fgetspent(file) +sgetspent(str) +putspent(entry,file) +lckpwdf,lock +ulckpwdf,unlock +lock? + +4. Structure + +Shadow::Passwd::Entry (Struct::PasswdEntry) + sp_namp - pointer to null-terminated user name. + sp_pwdp - pointer to null-terminated password. + sp_lstchg - days since Jan 1, 1970 password was last + changed. + sp_min - days before which password may not be changed. + sp_max - days after which password must be changed. + sp_warn - days before password is to expire that user is + warned of pending password expiration. + sp_inact - days after password expires that account is + considered inactive and disabled. + sp_expire - days since Jan 1, 1970 when account will be + + +5. ÀâÌÀ + +getspent, getspname, fgetspent, sgetspent¤ÏShadow::Passwd::Entry +¥¹¥È¥é¥¯¥Á¥ã¤òÊÖ¤·¤Þ¤¹¡£getspent ¤Ï¥Õ¥¡¥¤¥ë¤«¤é¼¡¤Î¥Ñ¥¹¥ï +¡¼¥É¥¨¥ó¥È¥ê¤òÊÖ¤·¡¢fgetspent ¤ÏÍ¿¤¨¤é¤ì¤¿IO¤«¤é¼¡¤Î¥¨¥ó¥È +¥ê¤òÊÖ¤·¤Þ¤¹¡£sgetspent¤ÏÍ¿¤¨¤é¤ì¤¿Ê¸»úÎ󤫤éShadow::Passwd::Entry +¥¹¥È¥é¥¯¥Á¥ã¤òÊÖ¤·¤Þ¤¹¡£getspnam¤Ï¥æ¡¼¥¶Ì¾¤òÍ¿¤¨¤ë¤È/etc/shadow +¤«¤é¤½¤Î¥æ¡¼¥¶¤ÎShadow::Passwd::Entry¥¹¥È¥é¥¯¥Á¥ã¤òÊÖ¤·¤Þ¤¹¡£ +¥Õ¥¡¥¤¥ë¤Î½ªÃ¼¤Ë㤹¤ë¤Ènil¤ÎÃͤòÊÖ¤·¤Þ¤¹¡£ + +setspent,endspent¤Ï¤½¤ì¤¾¤ì¡¢¥Õ¥¡¥¤¥ë¤Ø¤Î¥¢¥¯¥»¥¹¤Î¤Ï¤¸¤á¤È +¤ª¤ï¤ê¤Ë»È¤¤¤Þ¤¹¡£ + +lckpwdf(lock),ulckpwdf(unlock)¤Ï/etc/shadow¤Ø¤ÎÇÓ¾Ū¥¢¥¯¥»¥¹ +¤ò¼Â¸½¤¹¤ë¤¿¤á¤Ë¤¢¤ê¤Þ¤¹¡£ +lock¤Ë¼ºÇÔ¤¹¤ë¤ÈShadow::FileLock¤È¤¤¤¦Îã³°¤òȯÀ¸¤µ¤»¤Þ¤¹¡£ +lock¤ò¥¤¥Æ¥ì¡¼¥¿¤È¤·¤Æ»È¤¦¤³¤È¤Ë¤è¤Ã¤Æ¡¢¥¤¥Æ¥ì¡¼¥¿¥Ö¥í¥Ã¥¯¤òÈ´¤±¤ë +¤È¤­¤Ë¼«Æ°Åª¤Ëunlock¤ò¹Ô¤Ê¤¤¤Þ¤¹¡£ + + +6. »²¹Í + +* man shadow +* /usr/include/shadow.h + + + + ttate@jaist.ac.jp diff --git a/ruby-shadow/files/default/shadow-1.4.1/depend b/ruby-shadow/files/default/shadow-1.4.1/depend new file mode 100755 index 0000000..f5811a2 --- /dev/null +++ b/ruby-shadow/files/default/shadow-1.4.1/depend @@ -0,0 +1 @@ +shadow.o : shadow.c $(hdrdir)/ruby.h $(hdrdir)/rubyio.h diff --git a/ruby-shadow/files/default/shadow-1.4.1/extconf.rb b/ruby-shadow/files/default/shadow-1.4.1/extconf.rb new file mode 100755 index 0000000..6f300e5 --- /dev/null +++ b/ruby-shadow/files/default/shadow-1.4.1/extconf.rb @@ -0,0 +1,26 @@ +# -*- ruby -*- +# extconf.rb +# +# Modified at: <1999/8/19 06:38:55 by ttate> +# + +require 'mkmf' + +$CFLAGS = "" +$LDFLAGS = "-lshadow" + +if( ! (ok = have_library("shadow","getspent")) ) + $LDFLAGS = "" + ok = have_func("getspent") +end + +ok &= have_func("sgetspent") +ok &= have_func("fgetspent") +ok &= have_func("setspent") +ok &= have_func("endspent") +ok &= have_func("lckpwdf") +ok &= have_func("ulckpwdf") + +if ok + create_makefile("shadow") +end diff --git a/ruby-shadow/files/default/shadow-1.4.1/shadow.c b/ruby-shadow/files/default/shadow-1.4.1/shadow.c new file mode 100755 index 0000000..8a52c83 --- /dev/null +++ b/ruby-shadow/files/default/shadow-1.4.1/shadow.c @@ -0,0 +1,281 @@ +/* + * shadow.c + * + * Ruby extention module for using Linux shadow password. + * + * Copyright (C) 1998-1999 by Takaaki.Tateishi(ttate@jaist.ac.jp) + * License: Free for any use with your own risk! + * Modified at: <1999/8/19 06:48:18 by ttate> + */ + +#include +#include "ruby.h" +#include "rubyio.h" + +static VALUE rb_mShadow; +static VALUE rb_mPasswd; +static VALUE rb_sPasswdEntry; +static VALUE rb_mGroup; +static VALUE rb_sGroupEntry; +static VALUE rb_eFileLock; + + +static VALUE +rb_shadow_setspent(VALUE self) +{ + setspent(); + return Qnil; +}; + + +static VALUE +rb_shadow_endspent(VALUE self) +{ + endspent(); + return Qnil; +}; + + +static VALUE +rb_shadow_sgetspent(VALUE self, VALUE str) +{ + struct spwd *entry; + VALUE result; + + if( TYPE(str) != T_STRING ) + rb_raise(rb_eException,"argument must be a string."); + + entry = sgetspent(STR2CSTR(str)); + + if( entry == NULL ) + return Qnil; + + result = rb_struct_new(rb_sPasswdEntry, + rb_tainted_str_new2(entry->sp_namp), + rb_tainted_str_new2(entry->sp_pwdp), + INT2FIX(entry->sp_lstchg), + INT2FIX(entry->sp_min), + INT2FIX(entry->sp_max), + INT2FIX(entry->sp_warn), + INT2FIX(entry->sp_inact), + INT2FIX(entry->sp_expire), + INT2FIX(entry->sp_flag), + 0); + free(entry); + return result; +}; + +static VALUE +rb_shadow_fgetspent(VALUE self, VALUE file) +{ + struct spwd *entry; + VALUE result; + + if( TYPE(file) != T_FILE ) + rb_raise(rb_eTypeError,"argument must be a File."); + + entry = fgetspent((RFILE(file)->fptr)->f); + + if( entry == NULL ) + return Qnil; + + result = rb_struct_new(rb_sPasswdEntry, + rb_tainted_str_new2(entry->sp_namp), + rb_tainted_str_new2(entry->sp_pwdp), + INT2FIX(entry->sp_lstchg), + INT2FIX(entry->sp_min), + INT2FIX(entry->sp_max), + INT2FIX(entry->sp_warn), + INT2FIX(entry->sp_inact), + INT2FIX(entry->sp_expire), + INT2FIX(entry->sp_flag), + 0); + return result; +}; + +static VALUE +rb_shadow_getspent(VALUE self) +{ + struct spwd *entry; + VALUE result; + + entry = getspent(); + + if( entry == NULL ) + return Qnil; + + result = rb_struct_new(rb_sPasswdEntry, + rb_tainted_str_new2(entry->sp_namp), + rb_tainted_str_new2(entry->sp_pwdp), + INT2FIX(entry->sp_lstchg), + INT2FIX(entry->sp_min), + INT2FIX(entry->sp_max), + INT2FIX(entry->sp_warn), + INT2FIX(entry->sp_inact), + INT2FIX(entry->sp_expire), + INT2FIX(entry->sp_flag), + 0); + return result; +}; + +static VALUE +rb_shadow_getspnam(VALUE self, VALUE name) +{ + struct spwd *entry; + VALUE result; + + if( TYPE(name) != T_STRING ) + rb_raise(rb_eException,"argument must be a string."); + + entry = getspnam(STR2CSTR(name)); + + if( entry == NULL ) + return Qnil; + + result = rb_struct_new(rb_sPasswdEntry, + rb_tainted_str_new2(entry->sp_namp), + rb_tainted_str_new2(entry->sp_pwdp), + INT2FIX(entry->sp_lstchg), + INT2FIX(entry->sp_min), + INT2FIX(entry->sp_max), + INT2FIX(entry->sp_warn), + INT2FIX(entry->sp_inact), + INT2FIX(entry->sp_expire), + INT2FIX(entry->sp_flag), + 0); + return result; +}; + + +static VALUE +rb_shadow_putspent(VALUE self, VALUE entry, VALUE file) +{ + struct spwd centry; + FILE* cfile; + VALUE val[9]; + int i; + int result; + + for(i=0; i<=8; i++) + val[i] = RSTRUCT(entry)->ptr[i]; + cfile = RFILE(file)->fptr->f; + + centry.sp_namp = STR2CSTR(val[0]); + centry.sp_pwdp = STR2CSTR(val[1]); + centry.sp_lstchg = FIX2INT(val[2]); + centry.sp_min = FIX2INT(val[3]); + centry.sp_max = FIX2INT(val[4]); + centry.sp_warn = FIX2INT(val[5]); + centry.sp_inact = FIX2INT(val[6]); + centry.sp_expire = FIX2INT(val[7]); + centry.sp_flag = FIX2INT(val[8]); + + result = putspent(¢ry,cfile); + + if( result == -1 ) + rb_raise(rb_eStandardError,"can't change password"); + + return Qtrue; +}; + + +static VALUE +rb_shadow_lckpwdf(VALUE self) +{ + int result; + result = lckpwdf(); + if( result == -1 ) + rb_raise(rb_eFileLock,"password file was locked"); + else + return Qtrue; +}; + +static int in_lock; + +static VALUE +rb_shadow_lock(VALUE self) +{ + int result; + + if( rb_iterator_p() ){ + result = lckpwdf(); + if( result == -1 ){ + rb_raise(rb_eFileLock,"password file was locked"); + } + else{ + in_lock++; + rb_yield(Qnil); + in_lock--; + ulckpwdf(); + }; + return Qtrue; + } + else{ + return rb_shadow_lckpwdf(self); + }; +}; + + +static VALUE +rb_shadow_ulckpwdf(VALUE self) +{ + if( in_lock ){ + rb_raise(rb_eFileLock,"you call unlock method in lock iterator."); + }; + ulckpwdf(); + return Qtrue; +}; + +static VALUE +rb_shadow_unlock(VALUE self) +{ + return rb_shadow_ulckpwdf(self); +}; + +static VALUE +rb_shadow_lock_p(VALUE self) +{ + int result; + + result = lckpwdf(); + if( result == -1 ){ + return Qtrue; + } + else{ + ulckpwdf(); + return Qfalse; + }; +}; + + +void +Init_shadow() +{ + rb_sPasswdEntry = rb_struct_define("PasswdEntry", + "sp_namp","sp_pwdp","sp_lstchg", + "sp_min","sp_max","sp_warn", + "sp_inact","sp_expire","sp_flag",0); + rb_sGroupEntry = rb_struct_define("GroupEntry", + "sg_name","sg_passwd", + "sg_adm","sg_mem",0); + + rb_mShadow = rb_define_module("Shadow"); + rb_eFileLock = rb_define_class_under(rb_mShadow,"FileLock",rb_eException); + rb_mPasswd = rb_define_module_under(rb_mShadow,"Passwd"); + rb_define_const(rb_mPasswd,"Entry",rb_sPasswdEntry); + rb_mGroup = rb_define_module_under(rb_mShadow,"Group"); + rb_define_const(rb_mGroup,"Entry",rb_sGroupEntry); + + rb_define_module_function(rb_mPasswd,"setspent",rb_shadow_setspent,0); + rb_define_module_function(rb_mPasswd,"endspent",rb_shadow_endspent,0); + rb_define_module_function(rb_mPasswd,"sgetspent",rb_shadow_sgetspent,1); + rb_define_module_function(rb_mPasswd,"fgetspent",rb_shadow_fgetspent,1); + rb_define_module_function(rb_mPasswd,"getspent",rb_shadow_getspent,0); + rb_define_module_function(rb_mPasswd,"getspnam",rb_shadow_getspnam,1); + rb_define_module_function(rb_mPasswd,"putspent",rb_shadow_putspent,2); + rb_define_module_function(rb_mPasswd,"lckpwdf",rb_shadow_lckpwdf,0); + rb_define_module_function(rb_mPasswd,"lock",rb_shadow_lock,0); + rb_define_module_function(rb_mPasswd,"ulckpwdf",rb_shadow_ulckpwdf,0); + rb_define_module_function(rb_mPasswd,"unlock",rb_shadow_unlock,0); + rb_define_module_function(rb_mPasswd,"lock?",rb_shadow_lock_p,0); +}; diff --git a/ruby-shadow/recipes/default.rb b/ruby-shadow/recipes/default.rb new file mode 100755 index 0000000..d9236d3 --- /dev/null +++ b/ruby-shadow/recipes/default.rb @@ -0,0 +1,15 @@ +remote_directory "/usr/local/src/shadow-1.4.1" do + source 'shadow-1.4.1' + not_if { File.exists?(File.join(node[:ruby_shadow][:site_ruby], "#{node[:languages][:ruby][:platform]}/shadow.so")) } +end + +bash "install ruby shadow library" do + user "root" + cwd "/usr/local/src" + code <<-EOH + cd shadow-1.4.1 + ruby extconf.rb + make install + EOH + not_if { File.exists?(File.join(node[:ruby_shadow][:site_ruby], "/#{node[:languages][:ruby][:platform]}/shadow.so")) } +end \ No newline at end of file diff --git a/ruby/metadata.json b/ruby/metadata.json new file mode 100644 index 0000000..e5e6eed --- /dev/null +++ b/ruby/metadata.json @@ -0,0 +1,52 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs ruby packages", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "ruby": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "fedora": [ + + ], + "centos": [ + + ], + "debian": [ + + ], + "redhat": [ + + ] + }, + "version": "0.7.0", + "name": "ruby", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "ruby": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/ruby/metadata.rb b/ruby/metadata.rb new file mode 100644 index 0000000..45889d4 --- /dev/null +++ b/ruby/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs ruby packages" +version "0.7" + +%w{ centos redhat fedora ubuntu debian }.each do |os| + supports os +end diff --git a/ruby/recipes/default.rb b/ruby/recipes/default.rb new file mode 100644 index 0000000..09f98b3 --- /dev/null +++ b/ruby/recipes/default.rb @@ -0,0 +1,49 @@ +# +# Cookbook Name:: ruby +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "ruby" do + action :install +end + +extra_packages = case node[:platform] + when "ubuntu","debian" + %w{ + ruby1.8 + ruby1.8-dev + rdoc1.8 + ri1.8 + libopenssl-ruby + } + when "centos","redhat","fedora" + %w{ + ruby-libs + ruby-devel + ruby-docs + ruby-ri + ruby-irb + ruby-rdoc + ruby-mode + } + end + +extra_packages.each do |pkg| + package pkg do + action :install + end +end diff --git a/ruby_enterprise/README.rdoc b/ruby_enterprise/README.rdoc new file mode 100644 index 0000000..476cf5d --- /dev/null +++ b/ruby_enterprise/README.rdoc @@ -0,0 +1,53 @@ += DESCRIPTION: + +Installs Ruby Enterprise Edition (REE) from Phusion. + += REQUIREMENTS: + +Opscode's build-essential cookbook to get a compiler and associated files installed. + += ATTRIBUTES: + +* ruby_enterprise[:install_path] - Location to install REE. Default /opt/ruby-enterprise +* ruby_enterprise[:version] - Version-datestamp to use. Default 1.8.6-20090610. May lag behind latest REE release. +* ruby_enterprise[:url] - URL to download. Default is from RubyForge, with the version specified. Note the download ID must be updated when there are new releases. + += USAGE: + +Include the ruby_enterprise recipe to install REE. + + include_recipe "ruby_enterprise" + +Or add it to your role, or directly to a node's recipes. + +Install RubyGems under REE with the ree_gem definition. + + ree_gem "rails" do + source "http://gems.rubyforge.org" + version "2.3.4" + end + +The definition supports parameters for source and version, though they are optional. + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () +Author:: Sean Cribbs () +Author:: Michael Hale () + +Copyright:: 2009-2010, Opscode, Inc. +Copyright:: 2009, Sean Cribbs +Copyright:: 2009, Michael Hale + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/ruby_enterprise/attributes/ruby_enterprise.rb b/ruby_enterprise/attributes/ruby_enterprise.rb new file mode 100644 index 0000000..ecb4b87 --- /dev/null +++ b/ruby_enterprise/attributes/ruby_enterprise.rb @@ -0,0 +1,30 @@ +# +# Cookbook Name:: ruby_enterprise +# attributes:: ruby_enterprise +# +# Author:: Joshua Timberman () +# Author:: Sean Cribbs () +# Author:: Michael Hale () +# +# Copyright:: 2009-2010, Opscode, Inc. +# Copyright:: 2009, Sean Cribbs +# Copyright:: 2009, Michael Hale +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set_unless[:ruby_enterprise][:install_path] = "/opt/ruby-enterprise" +set_unless[:ruby_enterprise][:ruby_bin] = "/opt/ruby-enterprise/bin/ruby" +set_unless[:ruby_enterprise][:gems_dir] = "#{ruby_enterprise[:install_path]}/lib/ruby/gems/1.8" +set_unless[:ruby_enterprise][:version] = '1.8.7-2009.10' +set_unless[:ruby_enterprise][:url] = "http://rubyforge.org/frs/download.php/66162/ruby-enterprise-#{ruby_enterprise[:version]}" diff --git a/ruby_enterprise/definitions/ree_gem.rb b/ruby_enterprise/definitions/ree_gem.rb new file mode 100644 index 0000000..200ee21 --- /dev/null +++ b/ruby_enterprise/definitions/ree_gem.rb @@ -0,0 +1,28 @@ +# +# Cookbook Name:: ruby_enterprise +# Recipe:: ree_gem +# +# Author:: Joshua Timberman () +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :ree_gem, :source => nil, :version => nil do + gem_package params[:name] do + gem_binary "#{node[:ruby_enterprise][:install_path]}/bin/gem" + source params[:source] if params[:source] + version params[:version] if params[:version] + end +end diff --git a/ruby_enterprise/metadata.json b/ruby_enterprise/metadata.json new file mode 100644 index 0000000..94ac30b --- /dev/null +++ b/ruby_enterprise/metadata.json @@ -0,0 +1,45 @@ +{ + "replacing": { + + }, + "dependencies": { + "build-essential": [ + + ] + }, + "groupings": { + + }, + "long_description": "= DESCRIPTION:\n\nInstalls Ruby Enterprise Edition (REE) from Phusion.\n\n= REQUIREMENTS:\n\nOpscode's build-essential cookbook to get a compiler and associated files installed.\n\n= ATTRIBUTES: \n\n* ruby_enterprise[:install_path] - Location to install REE. Default /opt/ruby-enterprise\n* ruby_enterprise[:version] - Version-datestamp to use. Default 1.8.6-20090610. May lag behind latest REE release.\n* ruby_enterprise[:url] - URL to download. Default is from RubyForge, with the version specified. Note the download ID must be updated when there are new releases.\n\n= USAGE:\n\nInclude the ruby_enterprise recipe to install REE.\n\n include_recipe \"ruby_enterprise\"\n\nOr add it to your role, or directly to a node's recipes.\n\nInstall RubyGems under REE with the ree_gem definition.\n\n ree_gem \"rails\" do\n source \"http://gems.rubyforge.org\"\n version \"2.3.4\"\n end\n\nThe definition supports parameters for source and version, though they are optional.\n\n= LICENSE and AUTHOR:\n\nAuthor:: Joshua Timberman ()\nCopyright:: 2009, Opscode, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n", + "description": "Installs/Configures ruby-enterprise", + "recommendations": { + + }, + "platforms": { + "ubuntu": [ + + ] + }, + "version": "0.1.0", + "maintainer": "Opscode, Inc.", + "name": "ruby_enterprise", + "suggestions": { + + }, + "maintainer_email": "ops@opscode.com", + "recipes": { + "ruby_enterprise": "" + }, + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "ruby_enterprise": [ + + ] + }, + "license": "Apache 2.0" +} \ No newline at end of file diff --git a/ruby_enterprise/metadata.rb b/ruby_enterprise/metadata.rb new file mode 100644 index 0000000..e28cbd7 --- /dev/null +++ b/ruby_enterprise/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "ops@opscode.com" +license "Apache 2.0" +description "Installs/Configures ruby-enterprise" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.1" + +depends "build-essential" +supports "ubuntu" diff --git a/ruby_enterprise/recipes/default.rb b/ruby_enterprise/recipes/default.rb new file mode 100644 index 0000000..3969f18 --- /dev/null +++ b/ruby_enterprise/recipes/default.rb @@ -0,0 +1,49 @@ +# +# Cookbook Name:: ruby_enterprise +# Recipe:: default +# +# Author:: Joshua Timberman () +# Author:: Sean Cribbs () +# Author:: Michael Hale () +# +# Copyright:: 2009-2010, Opscode, Inc. +# Copyright:: 2009, Sean Cribbs +# Copyright:: 2009, Michael Hale +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "build-essential" + +%w{ libssl-dev libreadline5-dev }.each do |pkg| + package pkg +end + +remote_file "/tmp/ruby-enterprise-#{node[:ruby_enterprise][:version]}.tar.gz" do + source "#{node[:ruby_enterprise][:url]}.tar.gz" + not_if { ::File.exists?("/tmp/ruby-enterprise-#{node[:ruby_enterprise][:version]}.tar.gz") } +end + +bash "Install Ruby Enterprise Edition" do + cwd "/tmp" + code <<-EOH + tar zxf ruby-enterprise-#{node[:ruby_enterprise][:version]}.tar.gz + ruby-enterprise-#{node[:ruby_enterprise][:version]}/installer \ + --auto=#{node[:ruby_enterprise][:install_path]} \ + --dont-install-useful-gems + EOH + not_if do + ::File.exists?("#{node[:ruby_enterprise][:install_path]}/bin/ree-version") && + system("#{node[:ruby_enterprise][:install_path]}/bin/ree-version | grep -q '#{node[:ruby_enterprise][:version]}$'") + end +end diff --git a/ruby_enterprise_edition/attributes/ree.rb b/ruby_enterprise_edition/attributes/ree.rb new file mode 100755 index 0000000..9436f77 --- /dev/null +++ b/ruby_enterprise_edition/attributes/ree.rb @@ -0,0 +1,3 @@ +default.ree[:version] = "1.8.7-2009.10" +#default.ree[:version] = "1.8.6-20090610" +default.ree[:architecture] = kernel[:machine] == "x86_64" ? "amd64" : "i386" \ No newline at end of file diff --git a/ruby_enterprise_edition/recipes/default.rb b/ruby_enterprise_edition/recipes/default.rb new file mode 100755 index 0000000..62e545a --- /dev/null +++ b/ruby_enterprise_edition/recipes/default.rb @@ -0,0 +1,9 @@ +ree_filename = ["ruby-enterprise", node[:ree][:version], node[:ree][:architecture]].join("_")+".deb" + +remote_file "/tmp/#{ree_filename}" do + source ree_filename +end + +dpkg_package "ruby-enterprise" do + source "/tmp/#{ree_filename}" +end \ No newline at end of file diff --git a/rubygems/metadata.rb b/rubygems/metadata.rb new file mode 100644 index 0000000..70d5028 --- /dev/null +++ b/rubygems/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Opscode rubygems config" +long_description <<-EOH +Configures rubygems sources lists +EOH +version "0.1" +recipe "rubygems", "Configures rubygems" diff --git a/rubygems/recipes/default.rb b/rubygems/recipes/default.rb new file mode 100644 index 0000000..a178b9b --- /dev/null +++ b/rubygems/recipes/default.rb @@ -0,0 +1,25 @@ +# +# Author:: Adam Jacob +# Cookbook Name:: rubygems +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +%w{ rubyforge.org opscode.com }.each do |domain| + execute "gem sources --add http://gems.#{domain}" do + not_if "gem sources --list | grep gems.#{domain}" + end +end diff --git a/runit/attributes/sv_bin.rb b/runit/attributes/sv_bin.rb new file mode 100644 index 0000000..84a09b3 --- /dev/null +++ b/runit/attributes/sv_bin.rb @@ -0,0 +1,25 @@ +# +# Cookbook Name:: runit +# Attribute File:: sv_bin +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case platform +when "ubuntu","debian" + set[:runit][:sv_bin] = "/usr/bin/sv" + set[:runit][:service_dir] = "/etc/service" + set[:runit][:sv_dir] = "/etc/sv" +end diff --git a/runit/definitions/runit_service.rb b/runit/definitions/runit_service.rb new file mode 100644 index 0000000..bcb1ad9 --- /dev/null +++ b/runit/definitions/runit_service.rb @@ -0,0 +1,87 @@ +# +# Cookbook Name:: runit +# Definition:: runit_service +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :runit_service, :directory => nil, :only_if => false, :options => Hash.new do + + params[:directory] ||= node[:runit][:sv_dir] + + sv_dir_name = "#{params[:directory]}/#{params[:name]}" + + directory sv_dir_name do + mode 0755 + action :create + end + + directory "#{sv_dir_name}/log" do + mode 0755 + action :create + end + + directory "#{sv_dir_name}/log/main" do + mode 0755 + action :create + end + + params[:template_name] ||= params[:name] + template "#{sv_dir_name}/run" do + mode 0755 + source "sv-#{params[:template_name]}-run.erb" + cookbook params[:cookbook] if params[:cookbook] + if params[:options].respond_to?(:has_key?) + variables :options => params[:options] + end + end + + template "#{sv_dir_name}/log/run" do + mode 0755 + source "sv-#{params[:template_name]}-log-run.erb" + cookbook params[:cookbook] if params[:cookbook] + if params[:options].respond_to?(:has_key?) + variables :options => params[:options] + end + end + + link "/etc/init.d/#{params[:name]}" do + to node[:runit][:sv_bin] + end + + link "#{node[:runit][:service_dir]}/#{params[:name]}" do + to "#{sv_dir_name}" + end + + ruby_block "supervise_#{params[:name]}_sleep" do + block do + (1..6).each {|i| sleep 1 unless ::FileTest.pipe?("#{sv_dir_name}/supervise/ok") } + end + not_if { FileTest.pipe?("#{sv_dir_name}/supervise/ok") } + end + + service params[:name] do + supports :restart => true, :status => true + subscribes :restart, resources(:template => "#{sv_dir_name}/run"), :delayed + subscribes :restart, resources(:template => "#{sv_dir_name}/log/run"), :delayed + action :nothing + end + + #execute "#{params[:name]}-down" do + # command "/etc/init.d/#{params[:name]} down" + # only_if do params[:only_if] end + #end + +end diff --git a/runit/files/default/runsvdir b/runit/files/default/runsvdir new file mode 100644 index 0000000..e69de29 diff --git a/runit/files/ubuntu-6.10/runsvdir b/runit/files/ubuntu-6.10/runsvdir new file mode 100644 index 0000000..4040e34 --- /dev/null +++ b/runit/files/ubuntu-6.10/runsvdir @@ -0,0 +1,6 @@ +start on runlevel-2 +start on runlevel-3 +start on runlevel-4 +start on runlevel-5 +stop on shutdown +respawn /usr/sbin/runsvdir-start diff --git a/runit/files/ubuntu-7.04/runsvdir b/runit/files/ubuntu-7.04/runsvdir new file mode 100644 index 0000000..ee173c9 --- /dev/null +++ b/runit/files/ubuntu-7.04/runsvdir @@ -0,0 +1,7 @@ +start on runlevel 2 +start on runlevel 3 +start on runlevel 4 +start on runlevel 5 +stop on shutdown +respawn +exec /usr/sbin/runsvdir-start diff --git a/runit/files/ubuntu-7.10/runsvdir b/runit/files/ubuntu-7.10/runsvdir new file mode 100644 index 0000000..ee173c9 --- /dev/null +++ b/runit/files/ubuntu-7.10/runsvdir @@ -0,0 +1,7 @@ +start on runlevel 2 +start on runlevel 3 +start on runlevel 4 +start on runlevel 5 +stop on shutdown +respawn +exec /usr/sbin/runsvdir-start diff --git a/runit/files/ubuntu-8.04/runsvdir b/runit/files/ubuntu-8.04/runsvdir new file mode 100644 index 0000000..ee173c9 --- /dev/null +++ b/runit/files/ubuntu-8.04/runsvdir @@ -0,0 +1,7 @@ +start on runlevel 2 +start on runlevel 3 +start on runlevel 4 +start on runlevel 5 +stop on shutdown +respawn +exec /usr/sbin/runsvdir-start diff --git a/runit/metadata.json b/runit/metadata.json new file mode 100644 index 0000000..2b3a13e --- /dev/null +++ b/runit/metadata.json @@ -0,0 +1,85 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs runit and provides runit_service definition", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "runit": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "runit", + "conflicting": { + + }, + "attributes": { + "runit\/service_dir": { + "default": "\/etc\/service", + "type": "string", + "multiple_values": false, + "description": "Symlinks to services managed under runit", + "display_name": "Runit service directory", + "recipes": [ + + ], + "required": false + }, + "runit\/sv_bin": { + "default": "\/usr\/bin\/sv", + "type": "string", + "multiple_values": false, + "description": "Location of the sv binary", + "display_name": "Runit sv bin", + "recipes": [ + + ], + "required": false + }, + "runit": { + "type": "hash", + "multiple_values": false, + "description": "Hash of runit attributes", + "display_name": "Runit", + "recipes": [ + + ], + "required": false + }, + "runit\/sv_dir": { + "default": "\/etc\/sv", + "type": "string", + "multiple_values": false, + "description": "Location of services managed by runit", + "display_name": "Runit sv directory", + "recipes": [ + + ], + "required": false + } + }, + "providing": { + "runit": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/runit/metadata.rb b/runit/metadata.rb new file mode 100644 index 0000000..7f3f925 --- /dev/null +++ b/runit/metadata.rb @@ -0,0 +1,30 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs runit and provides runit_service definition" +version "0.8" + +%w{ ubuntu debian }.each do |os| + supports os +end + +attribute "runit", + :display_name => "Runit", + :description => "Hash of runit attributes", + :type => "hash" + +attribute "runit/sv_bin", + :display_name => "Runit sv bin", + :description => "Location of the sv binary", + :default => "/usr/bin/sv" + +attribute "runit/service_dir", + :display_name => "Runit service directory", + :description => "Symlinks to services managed under runit", + :default => "/etc/service" + +attribute "runit/sv_dir", + :display_name => "Runit sv directory", + :description => "Location of services managed by runit", + :default => "/etc/sv" + diff --git a/runit/recipes/default.rb b/runit/recipes/default.rb new file mode 100644 index 0000000..10f6f9f --- /dev/null +++ b/runit/recipes/default.rb @@ -0,0 +1,46 @@ +# +# Cookbook Name:: runit +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node[:platform] +when "debian","ubuntu" + execute "start-runsvdir" do + command value_for_platform( + "debian" => { "default" => "runsvdir-start" }, + "ubuntu" => { "default" => "start runsvdir" } + ) + action :nothing + end + + package "runit" do + action :install + notifies value_for_platform( + "debian" => { "4.0" => :run, "default" => :nothing }, + "ubuntu" => { "default" => :run } + ), resources(:execute => "start-runsvdir") + end + + if node[:platform_version] <= "8.04" && node[:platform] =~ /ubuntu/i + remote_file "/etc/event.d/runsvdir" do + source "runsvdir" + mode 0644 + notifies :run, resources(:execute => "start-runsvdir") + only_if do File.directory?("/etc/event.d") end + end + end +end diff --git a/screen/metadata.json b/screen/metadata.json new file mode 100644 index 0000000..de1d7e5 --- /dev/null +++ b/screen/metadata.json @@ -0,0 +1,52 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs screen", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "screen": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "fedora": [ + + ], + "centos": [ + + ], + "debian": [ + + ], + "redhat": [ + + ] + }, + "version": "0.7.0", + "name": "screen", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "screen": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/screen/metadata.rb b/screen/metadata.rb new file mode 100644 index 0000000..5fac8e3 --- /dev/null +++ b/screen/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs screen" +version "0.7" + +%w{ redhat centos fedora ubuntu debian }.each do |os| + supports os +end diff --git a/screen/recipes/default.rb b/screen/recipes/default.rb new file mode 100644 index 0000000..0f81ff4 --- /dev/null +++ b/screen/recipes/default.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: screen +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "screen" do + action :install +end diff --git a/simple_rails_app/attributes/simple_rails_app.rb b/simple_rails_app/attributes/simple_rails_app.rb new file mode 100644 index 0000000..0c79131 --- /dev/null +++ b/simple_rails_app/attributes/simple_rails_app.rb @@ -0,0 +1,2 @@ +set_unless[:nginx][:conf_dir] = "/etc/nginx" +set_unless[:apps] = [{ :name => "enki", :username => "site", :git_branch => "master"}] \ No newline at end of file diff --git a/simple_rails_app/recipes/default.rb b/simple_rails_app/recipes/default.rb new file mode 100644 index 0000000..48fe684 --- /dev/null +++ b/simple_rails_app/recipes/default.rb @@ -0,0 +1,51 @@ +include_recipe "git" + +node[:apps].each do |app| + home_path = "/home/#{app[:username]}" + repos_path = "#{home_path}/repos/#{app[:name]}.git" + app_path = "#{home_path}/#{app[:name]}" + + # initialize bare git repo + bash "create repo folder" do + user app[:username] + code "mkdir -p #{repos_path} && cd #{repos_path} && git init --bare" + not_if { File.exists?(repos_path) } + end + + template "#{repos_path}/hooks/post-receive" do + path "#{repos_path}/hooks/post-receive" + source "post-receive.erb" + owner app[:username] + group app[:group] || app[:username] + mode 0755 + variables( + :app_path => app_path, + :git_branch => app[:git_branch] || "master" + ) + action :create + end + + # set web app permissions + bash "clone git repo" do + user app[:username] + code "cd #{home_path} && git clone #{repos_path} #{app[:name]}" + not_if { File.exists?(app_path) } + end + + template "#{node[:nginx][:conf_dir]}/sites-available/#{app[:name]}" do + source "rails_app.conf.erb" + owner "root" + group "root" + mode 0644 + variables( + :root_dir => app_path, + :server_name => app[:server] + ) + if File.exists?("#{node[:nginx][:conf_dir]}/sites-enabled/#{app[:name]}") + notifies :reload, resources(:service => "nginx"), :delayed + end + not_if { File.exists?("#{node[:nginx][:conf_dir]}/sites-enabled/#{app[:name]}") } + end + + nginx_site app[:name] +end \ No newline at end of file diff --git a/simple_rails_app/templates/default/post-receive.erb b/simple_rails_app/templates/default/post-receive.erb new file mode 100644 index 0000000..d36057b --- /dev/null +++ b/simple_rails_app/templates/default/post-receive.erb @@ -0,0 +1,23 @@ +#!/bin/sh +cd <%= @app_path %> +env -i [ -d log ] || mkdir log +env -i [ -d tmp ] || mkdir tmp +env -i git reset --hard +env -i git pull origin <%= @git_branch %> +env -i [ -f .gitmodules ] && git submodule update +env -i rake db:migrate RAILS_ENV=production +env -i touch tmp/restart.txt + +# +# An example hook script for the post-receive event +# +# This script is run after receive-pack has accepted a pack and the +# repository has been updated. It is passed arguments in through stdin +# in the form +# +# For example: +# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master +# +# see contrib/hooks/ for an sample, or uncomment the next line (on debian) +# +#. /usr/share/doc/git-core/contrib/hooks/post-receive-email diff --git a/simple_rails_app/templates/default/rails_app_conf.erb b/simple_rails_app/templates/default/rails_app_conf.erb new file mode 100644 index 0000000..7faa55b --- /dev/null +++ b/simple_rails_app/templates/default/rails_app_conf.erb @@ -0,0 +1,13 @@ +server { + server_name <%= @server_name %>; + listen 80; + + location / { + root <%= @root_dir %>/public; + passenger_enabled on; + + if (-f $document_root/system/maintenance.html) { + rewrite ^(.*)$ /system/maintenance.html break; + } + } +} diff --git a/solr/README.rdoc b/solr/README.rdoc new file mode 100644 index 0000000..dea8cc5 --- /dev/null +++ b/solr/README.rdoc @@ -0,0 +1,57 @@ += DESCRIPTION: + +Sets up user and environment for running solr instances. + += REQUIREMENTS: + +== Platform and Application Environment: + +Tested on Ubuntu 8.10. May work on other platforms, esp Ubuntu/Debian. + +Requires solr installed, such as a vendor plugin for a Rails application. Assumes 'start.jar' exists. Also requires ssh keys for solr user. See usage. + +== Cookbooks: + +Opscode cookbooks, http://github.com/opscode/cookbooks/tree/master: + +* capistrano (capistrano_setup) +* java +* runit (runit_service) + += ATTRIBUTES: + +* solr[:user] - username for solr process and files/dirs. +* solr[:uid] - UID for solr user. +* solr[:group] - group name for solr files/dirs. +* solr[:gid] - GID for solr group. + += USAGE: + +To create a solr instance for an application, use the solr_instance define: + + solr_instance "my_app" + +The recipe assumes that id_rsa ssh key pair has been created for the solr user. The files should be located in the cookbook where the solr_instance is used (for example a site-cookbook). + +Also create the runit run and log run templates. For now the directory to cd into needs to be specified in the run template. See the sample in this cookbook. + +== Parameters: + +Optionally specify a cookbook where the ssh keypair is located, otherwise generate keys and put the files in the solr cookbook. Empty files are located there for placeholders. + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/solr/attributes/solr.rb b/solr/attributes/solr.rb new file mode 100644 index 0000000..21b1650 --- /dev/null +++ b/solr/attributes/solr.rb @@ -0,0 +1,23 @@ +# +# Cookbook Name:: solr +# Attributes:: solr +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set_unless[:solr][:user] = 'solr' +set_unless[:solr][:uid] = 551 +set_unless[:solr][:group] = 'solr' +set_unless[:solr][:gid] = 551 diff --git a/solr/definitions/solr_instance.rb b/solr/definitions/solr_instance.rb new file mode 100644 index 0000000..0d7cbe0 --- /dev/null +++ b/solr/definitions/solr_instance.rb @@ -0,0 +1,65 @@ +# +# Cookbook Name:: solr +# Definition:: solr_instance +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :solr_instance, :path => "/srv", :type => "master" do + include_recipe "solr" + + cap_setup "#{params[:name]}" do + path "#{params[:path]}/#{params[:name]}" + appowner "solr" + end + + %w{ solr solr/data }.each do |dir| + directory "#{params[:path]}/#{params[:name]}/#{dir}" do + owner node[:solr][:user] + group node[:solr][:group] + mode 0755 + end + end + + %w{ bin conf }.each do |dir| + directory "#{params[:path]}/#{params[:name]}/#{dir}" do + owner "root" + group "nogroup" + mode 0755 + end + end + + runit_service "#{params[:name]}_solr" + + directory "#{params[:path]}/#{params[:name]}/.ssh" do + owner node[:solr][:user] + group node[:solr][:group] + mode 0700 + end + + %w{ id_rsa id_rsa.pub authorized_keys }.each do |ssh_file| + remote_file "#{params[:path]}/#{params[:name]}/.ssh/#{ssh_file}" do + source ssh_file + owner node[:solr][:user] + group node[:solr][:group] + mode 0600 + if params[:cookbook] + cookbook params[:cookbook] + else + cookbook "solr" + end + end + end +end diff --git a/solr/files/default/authorized_keys b/solr/files/default/authorized_keys new file mode 100644 index 0000000..e69de29 diff --git a/solr/files/default/id_rsa b/solr/files/default/id_rsa new file mode 100644 index 0000000..e69de29 diff --git a/solr/files/default/id_rsa.pub b/solr/files/default/id_rsa.pub new file mode 100644 index 0000000..e69de29 diff --git a/solr/metadata.json b/solr/metadata.json new file mode 100644 index 0000000..643ca4e --- /dev/null +++ b/solr/metadata.json @@ -0,0 +1,96 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Sets up environment for solr instances", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "solr": "" + }, + "suggestions": { + "ruby": [ + + ] + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "solr", + "conflicting": { + + }, + "attributes": { + "solr\/group": { + "default": "solr", + "type": "string", + "multiple_values": false, + "description": "Group for solr instance", + "display_name": "Solr Group", + "recipes": [ + + ], + "required": false + }, + "solr\/uid": { + "default": "551", + "type": "string", + "multiple_values": false, + "description": "UID for solr instance", + "display_name": "Solr UID", + "recipes": [ + + ], + "required": false + }, + "solr\/user": { + "default": "solr", + "type": "string", + "multiple_values": false, + "description": "Username for solr instance", + "display_name": "Solr User", + "recipes": [ + + ], + "required": false + }, + "solr\/gid": { + "default": "551", + "type": "string", + "multiple_values": false, + "description": "GID for solr instance", + "display_name": "Solr GID", + "recipes": [ + + ], + "required": false + } + }, + "providing": { + "solr": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nSets up user and environment for running solr instances.\n\n= REQUIREMENTS:\n\n== Platform and Application Environment:\n\nTested on Ubuntu 8.10. May work on other platforms, esp Ubuntu\/Debian.\n\nRequires solr installed, such as a vendor plugin for a Rails application. Assumes 'start.jar' exists. Also requires ssh keys for solr user. See usage.\n\n== Cookbooks:\n\nOpscode cookbooks, http:\/\/github.com\/opscode\/cookbooks\/tree\/master:\n\n* capistrano (capistrano_setup)\n* java\n* runit (runit_service)\n\n= ATTRIBUTES: \n\n* solr[:user] - username for solr process and files\/dirs.\n* solr[:uid] - UID for solr user.\n* solr[:group] - group name for solr files\/dirs.\n* solr[:gid] - GID for solr group.\n\n= USAGE:\n\nTo create a solr instance for an application, use the solr_instance define:\n\n solr_instance \"my_app\" \n\nThe recipe assumes that id_rsa ssh key pair has been created for the solr user. The files should be located in the cookbook where the solr_instance is used (for example a site-cookbook). \n\nAlso create the runit run and log run templates. For now the directory to cd into needs to be specified in the run template. See the sample in this cookbook.\n\n== Parameters:\n\nOptionally specify a cookbook where the ssh keypair is located, otherwise generate keys and put the files in the solr cookbook. Empty files are located there for placeholders.\n\n= LICENSE and AUTHOR:\n \nAuthor:: Joshua Timberman ()\nCopyright:: 2009, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "replacing": { + + }, + "dependencies": { + "java": [ + + ], + "runit": [ + + ], + "capistrano": [ + + ] + } +} \ No newline at end of file diff --git a/solr/metadata.rb b/solr/metadata.rb new file mode 100644 index 0000000..2ffd700 --- /dev/null +++ b/solr/metadata.rb @@ -0,0 +1,36 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Sets up environment for solr instances" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.7" +suggests "ruby" + +%w{ java capistrano runit }.each do |cb| + depends cb +end + +%w{ debian ubuntu }.each do |os| + supports os +end + +attribute "solr/user", + :display_name => "Solr User", + :description => "Username for solr instance", + :default => "solr" + +attribute "solr/uid", + :display_name => "Solr UID", + :description => "UID for solr instance", + :default => "551" + +attribute "solr/group", + :display_name => "Solr Group", + :description => "Group for solr instance", + :default => "solr" + +attribute "solr/gid", + :display_name => "Solr GID", + :description => "GID for solr instance", + :default => "551" + diff --git a/solr/recipes/default.rb b/solr/recipes/default.rb new file mode 100644 index 0000000..5e7d5c3 --- /dev/null +++ b/solr/recipes/default.rb @@ -0,0 +1,36 @@ +# +# Cookbook Name:: solr +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "java" +include_recipe "capistrano" +include_recipe "runit" + +group node[:solr][:group] do + gid node[:solr][:gid] + action :create +end + +user node[:solr][:user] do + comment "Solr replication" + home "/srv/solr" + shell "/bin/bash" + uid node[:solr][:uid] + gid node[:solr][:gid] + action :create +end diff --git a/solr/templates/default/sv-solr-log-run.erb b/solr/templates/default/sv-solr-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/solr/templates/default/sv-solr-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/solr/templates/default/sv-solr-run.erb b/solr/templates/default/sv-solr-run.erb new file mode 100644 index 0000000..ec8662c --- /dev/null +++ b/solr/templates/default/sv-solr-run.erb @@ -0,0 +1,8 @@ +#!/bin/sh + +export JAVA_HOME=/usr/java/jdk +cd # for now need the directory specified. +exec 2>&1 +exec \ + chpst -u <%= @node[:solr][:user] %> /usr/bin/java -Xms128M -Xmx1024M \ + -jar start.jar \ No newline at end of file diff --git a/sqlite/metadata.json b/sqlite/metadata.json new file mode 100644 index 0000000..caabab5 --- /dev/null +++ b/sqlite/metadata.json @@ -0,0 +1,43 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs sqlite", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "sqlite": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "sqlite", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "sqlite": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/sqlite/metadata.rb b/sqlite/metadata.rb new file mode 100644 index 0000000..4a903c6 --- /dev/null +++ b/sqlite/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs sqlite" +version "0.7" + +%w{ubuntu debian}.each do |os| + supports os +end diff --git a/sqlite/recipes/default.rb b/sqlite/recipes/default.rb new file mode 100644 index 0000000..f2f9daa --- /dev/null +++ b/sqlite/recipes/default.rb @@ -0,0 +1,26 @@ +# +# Cookbook Name:: sqlite +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "sqlite3" do + action :upgrade +end + +package "sqlite3-doc" do + action :upgrade +end diff --git a/ssh/attributes/ssh.rb b/ssh/attributes/ssh.rb new file mode 100755 index 0000000..d5341a0 --- /dev/null +++ b/ssh/attributes/ssh.rb @@ -0,0 +1,2 @@ +ssh Mash.new unless attribute?(:ssh) +sshd Mash.new unless attribute?(:sshd) diff --git a/ssh/files/default/known_hosts b/ssh/files/default/known_hosts new file mode 100755 index 0000000..10b0873 --- /dev/null +++ b/ssh/files/default/known_hosts @@ -0,0 +1 @@ +# place known_hosts from ssh-keyscan here diff --git a/ssh/metadata.json b/ssh/metadata.json new file mode 100755 index 0000000..99977e6 --- /dev/null +++ b/ssh/metadata.json @@ -0,0 +1,38 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "ssh::server": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures ssh", + "version": "0.1.0", + "name": "ssh", + "providing": { + "ssh::server": [ + + ] + } +} \ No newline at end of file diff --git a/ssh/metadata.rb b/ssh/metadata.rb new file mode 100755 index 0000000..d40f1e6 --- /dev/null +++ b/ssh/metadata.rb @@ -0,0 +1,4 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures ssh" +version "0.1" diff --git a/ssh/recipes/server.rb b/ssh/recipes/server.rb new file mode 100755 index 0000000..b34a502 --- /dev/null +++ b/ssh/recipes/server.rb @@ -0,0 +1,29 @@ +service "ssh" do + supports :restart => true, :reload => true + action :enable +end + +remote_file "/etc/ssh/known_hosts" do + source "known_hosts" + mode 0644 +end + +template "/etc/ssh/ssh_config" do + source "ssh_config.erb" + mode 0644 + owner "root" + group "root" +end + +template "/etc/ssh/sshd_config" do + source "sshd_config.erb" + mode 0644 + owner "root" + group "root" + notifies :restart, resources(:service => "ssh") +end + + +service "ssh" do + action :start +end \ No newline at end of file diff --git a/ssh/templates/default/ssh_config.erb b/ssh/templates/default/ssh_config.erb new file mode 100755 index 0000000..b72919f --- /dev/null +++ b/ssh/templates/default/ssh_config.erb @@ -0,0 +1,8 @@ +# +# Dynamically generated by Chef - local modifications will be replaced +# +Host * + SendEnv LANG LC_* + GSSAPIAuthentication yes + GSSAPIDelegateCredentials no + GlobalKnownHostsFile /etc/ssh/known_hosts \ No newline at end of file diff --git a/ssh/templates/default/sshd_config.erb b/ssh/templates/default/sshd_config.erb new file mode 100755 index 0000000..47526c7 --- /dev/null +++ b/ssh/templates/default/sshd_config.erb @@ -0,0 +1,45 @@ +# +# Dynamically generated by Chef - local modifications will be replaced +# + +Port 22 +Protocol 2 +HostKey /etc/ssh/ssh_host_rsa_key +HostKey /etc/ssh/ssh_host_dsa_key + +UsePrivilegeSeparation yes + +KeyRegenerationInterval 3600 +ServerKeyBits 768 + +SyslogFacility AUTH +LogLevel INFO + +LoginGraceTime 120 +PermitRootLogin no +StrictModes yes +RSAAuthentication yes +PubkeyAuthentication yes + +IgnoreRhosts yes +RhostsRSAAuthentication no +HostbasedAuthentication no +PermitEmptyPasswords no +ChallengeResponseAuthentication no +PasswordAuthentication no + +X11Forwarding yes +X11DisplayOffset 10 +PrintMotd no +PrintLastLog yes +TCPKeepAlive yes + +AcceptEnv LANG LC_* + +Subsystem sftp /usr/lib/openssh/sftp-server + +UsePAM yes + +<% if @node[:sshd][:max_startups] %> +MaxStartups <%= @node[:sshd][:max_startups] %> +<% end %> \ No newline at end of file diff --git a/ssh_keys/definitions/add_keys.rb b/ssh_keys/definitions/add_keys.rb new file mode 100755 index 0000000..e49842a --- /dev/null +++ b/ssh_keys/definitions/add_keys.rb @@ -0,0 +1,17 @@ +define :add_keys, :conf => {} do + + config = params[:conf] + name = params[:name] + keys = Mash.new + keys[name] = node[:ssh_keys][name] + + template "/home/#{name}/.ssh/authorized_keys" do + source "authorized_keys.erb" + action :create + cookbook "ssh_keys" + owner name + group config[:group] ? config[:group].to_s : name + variables(:keys => keys) + mode 0600 + end +end \ No newline at end of file diff --git a/ssh_keys/recipes/default.rb b/ssh_keys/recipes/default.rb new file mode 100755 index 0000000..e69de29 diff --git a/ssh_keys/templates/default/authorized_keys.erb b/ssh_keys/templates/default/authorized_keys.erb new file mode 100755 index 0000000..71491f4 --- /dev/null +++ b/ssh_keys/templates/default/authorized_keys.erb @@ -0,0 +1,4 @@ +<% @keys.each do |name, key| %> +# <%= name %> +<%= key %> +<% end %> \ No newline at end of file diff --git a/ssh_known_hosts/README.rdoc b/ssh_known_hosts/README.rdoc new file mode 100644 index 0000000..2b9bc68 --- /dev/null +++ b/ssh_known_hosts/README.rdoc @@ -0,0 +1,29 @@ += DESCRIPTION: + +Build /etc/ssh/known_hosts based on search indexes and build it based on data retrieved by ohai. + += REQUIREMENTS: + +== Platform: Doesn't matter, should work on anything. + += USAGE: + +Generates /etc/ssh/known_hosts based on search indexes for RSA keys. + += LICENSE and AUTHOR: + +Author:: Scott M. Likens () + +Copyright:: 2009, Scott M. Likens + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ssh_known_hosts/metadata.json b/ssh_known_hosts/metadata.json new file mode 100644 index 0000000..a6ebf95 --- /dev/null +++ b/ssh_known_hosts/metadata.json @@ -0,0 +1,53 @@ +{ + "maintainer": "Scott M. Likens", + "description": "Dyanmically generates \/etc\/ssh\/known_hosts based on search indexes", + "recommendations": { + + }, + "maintainer_email": "scott@likens.us", + "recipes": { + "ssh_known_hosts::default": "Dyanmically generates \/etc\/ssh\/known_hosts based on search indexes", + "ssh_known_hosts": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "fedora": [ + + ], + "centos": [ + + ], + "debian": [ + + ], + "redhat": [ + + ] + }, + "version": "0.1.0", + "name": "ssh_known_hosts", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "ssh_known_hosts": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION: \n\nBuild \/etc\/ssh\/known_hosts based on search indexes and build it based on data retrieved by ohai.\n\n= REQUIREMENTS: \n\n== Platform: Doesn't matter, should work on anything.\n\n= USAGE:\n\nGenerates \/etc\/ssh\/known_hosts based on search indexes for RSA keys.\n\n= LICENSE and AUTHOR: \n\nAuthor:: Scott M. Likens ()\n\nCopyright:: 2009, Scott M. Likens\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n \n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/ssh_known_hosts/metadata.rb b/ssh_known_hosts/metadata.rb new file mode 100644 index 0000000..dde60b3 --- /dev/null +++ b/ssh_known_hosts/metadata.rb @@ -0,0 +1,11 @@ +maintainer "Scott M. Likens" +maintainer_email "scott@likens.us" +license "Apache 2.0" +description "Dyanmically generates /etc/ssh/known_hosts based on search indexes" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.1" +recipe "ssh_known_hosts::default", "Dyanmically generates /etc/ssh/known_hosts based on search indexes" + +%w{ redhat centos fedora ubuntu debian }.each do |os| + supports os +end diff --git a/ssh_known_hosts/recipes/default.rb b/ssh_known_hosts/recipes/default.rb new file mode 100644 index 0000000..9cdda3c --- /dev/null +++ b/ssh_known_hosts/recipes/default.rb @@ -0,0 +1,31 @@ +# +# Cookbook Name:: ssh_known_hosts +# Recipe:: default +# +# Copyright 2009, Scott M. Likens +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +nodes = [] +search(:node, "*", %w{ keys_ssh_host_rsa_public ipaddress hostname }) {|n| nodes << n} + +template "/etc/ssh/ssh_known_hosts" do + source "known_hosts.erb" + mode 0440 + owner "root" + group "root" + variables( + :nodes => nodes + ) +end diff --git a/ssh_known_hosts/templates/default/known_hosts.erb b/ssh_known_hosts/templates/default/known_hosts.erb new file mode 100644 index 0000000..6dc7439 --- /dev/null +++ b/ssh_known_hosts/templates/default/known_hosts.erb @@ -0,0 +1,6 @@ +# THIS FILE IS MAINTAINED BY CHEF, DO NOT MODIFY AS IT WILL BE OVERWRITTEN +<% @nodes.each do |n| -%> + <% if n.has_key?('keys_ssh_host_rsa_public') && n['keys_ssh_host_rsa_public'].length > 0 -%> +<%= n['hostname'] %>,<%= n['ipaddress'] %> ssh-rsa <%= n['keys_ssh_host_rsa_public'] %> + <% end -%> +<% end -%> diff --git a/ssl_certificates/attributes/ssl_certificates.rb b/ssl_certificates/attributes/ssl_certificates.rb new file mode 100755 index 0000000..0b47245 --- /dev/null +++ b/ssl_certificates/attributes/ssl_certificates.rb @@ -0,0 +1 @@ +default.ssl_certificates[:path] = "/etc/ssl_certs" \ No newline at end of file diff --git a/ssl_certificates/definitions/ssl_certificate.rb b/ssl_certificates/definitions/ssl_certificate.rb new file mode 100755 index 0000000..41efd82 --- /dev/null +++ b/ssl_certificates/definitions/ssl_certificate.rb @@ -0,0 +1,34 @@ +define :ssl_certificate do + + name = params[:name] =~ /\*\.(.+)/ ? "#{$1}_wildcard" : params[:name] + + remote_file "#{node[:ssl_certificates][:path]}/#{name}.crt" do + source "#{name}.crt" + mode "0640" + cookbook "ssl_certificates" + owner "root" + group "www-data" + end + remote_file "#{node[:ssl_certificates][:path]}/#{name}.key" do + source "#{name}.key" + mode "0640" + cookbook "ssl_certificates" + owner "root" + group "www-data" + end + remote_file "#{node[:ssl_certificates][:path]}/#{name}_intermediate.crt" do + source "#{name}_intermediate.crt" + mode "0640" + cookbook "ssl_certificates" + owner "root" + group "www-data" + end + remote_file "#{node[:ssl_certificates][:path]}/#{name}_combined.crt" do + source "#{name}_combined.crt" + mode "0640" + cookbook "ssl_certificates" + owner "root" + group "www-data" + end + +end \ No newline at end of file diff --git a/ssl_certificates/metadata.json b/ssl_certificates/metadata.json new file mode 100755 index 0000000..dc13caf --- /dev/null +++ b/ssl_certificates/metadata.json @@ -0,0 +1,38 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "ssl_certificates": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures SSL certificate", + "version": "0.1.0", + "name": "ssl_certificates", + "providing": { + "ssl_certificates": [ + + ] + } +} \ No newline at end of file diff --git a/ssl_certificates/metadata.rb b/ssl_certificates/metadata.rb new file mode 100755 index 0000000..9dd92d2 --- /dev/null +++ b/ssl_certificates/metadata.rb @@ -0,0 +1,4 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures SSL certificate" +version "0.1" diff --git a/ssl_certificates/recipes/default.rb b/ssl_certificates/recipes/default.rb new file mode 100755 index 0000000..d45ff17 --- /dev/null +++ b/ssl_certificates/recipes/default.rb @@ -0,0 +1,5 @@ +directory node[:ssl_certificates][:path] do + mode "0750" + owner "root" + group "www-data" +end \ No newline at end of file diff --git a/stompserver/metadata.json b/stompserver/metadata.json new file mode 100644 index 0000000..bc29116 --- /dev/null +++ b/stompserver/metadata.json @@ -0,0 +1,48 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs stompserver and sets up a runit_service", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "stompserver": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "stompserver", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "stompserver": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + "packages": [ + + ], + "runit": [ + + ] + } +} \ No newline at end of file diff --git a/stompserver/metadata.rb b/stompserver/metadata.rb new file mode 100644 index 0000000..9640dc9 --- /dev/null +++ b/stompserver/metadata.rb @@ -0,0 +1,13 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs stompserver and sets up a runit_service" +version "0.7" + +%w{ packages runit }.each do |cb| + depends cb +end + +%w{ ubuntu debian }.each do |os| + supports os +end diff --git a/stompserver/recipes/default.rb b/stompserver/recipes/default.rb new file mode 100644 index 0000000..1347421 --- /dev/null +++ b/stompserver/recipes/default.rb @@ -0,0 +1,44 @@ +# +# Author:: Joshua Timberman +# Cookbook Name:: stompserver +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include_recipe "packages" + +if platform?("centos","redhat","debian","ubuntu") and dist_only? + package value_for_platform( + "centos" => { "default" => "rubygem-stompserver" }, + "redhat" => { "default" => "rubygem-stompserver" }, + "debian" => { "default" => "stompserver" }, + "ubuntu" => { "default" => "stompserver" } + ) + + service "stompserver" do + supports [ :restart, :reload, :status ] + action [ :enable, :start ] + end + + return +end + +include_recipe "runit" + +gem_package "stompserver" do + action :install +end + +runit_service "stompserver" diff --git a/stompserver/templates/default/port_stompserver.erb b/stompserver/templates/default/port_stompserver.erb new file mode 100644 index 0000000..26be7d7 --- /dev/null +++ b/stompserver/templates/default/port_stompserver.erb @@ -0,0 +1,2 @@ +# Stompserver +-A FWR -p tcp -m tcp --dport 61613 -j ACCEPT \ No newline at end of file diff --git a/stompserver/templates/default/sv-stompserver-log-run.erb b/stompserver/templates/default/sv-stompserver-log-run.erb new file mode 100644 index 0000000..9ec4380 --- /dev/null +++ b/stompserver/templates/default/sv-stompserver-log-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec svlogd -tt ./main + diff --git a/stompserver/templates/default/sv-stompserver-run.erb b/stompserver/templates/default/sv-stompserver-run.erb new file mode 100644 index 0000000..5d925cd --- /dev/null +++ b/stompserver/templates/default/sv-stompserver-run.erb @@ -0,0 +1,4 @@ +#!/bin/sh +PATH=/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin<% if @node[:languages][:ruby][:gems_dir] %>:<%= @node[:languages][:ruby][:gems_dir] %>/bin<% end -%> +exec 2>&1 +exec /usr/bin/env stompserver diff --git a/subversion/metadata.json b/subversion/metadata.json new file mode 100644 index 0000000..950207a --- /dev/null +++ b/subversion/metadata.json @@ -0,0 +1,52 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs subversion", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "subversion": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "fedora": [ + + ], + "centos": [ + + ], + "debian": [ + + ], + "redhat": [ + + ] + }, + "version": "0.7.0", + "name": "subversion", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "subversion": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/subversion/metadata.rb b/subversion/metadata.rb new file mode 100644 index 0000000..24916d3 --- /dev/null +++ b/subversion/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs subversion" +version "0.7" + +%w{ redhat centos fedora ubuntu debian }.each do |os| + supports os +end diff --git a/subversion/recipes/default.rb b/subversion/recipes/default.rb new file mode 100644 index 0000000..814d741 --- /dev/null +++ b/subversion/recipes/default.rb @@ -0,0 +1,39 @@ +# +# Cookbook Name:: subversion +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0c +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "subversion" do + action :install +end + +extra_packages = case node[:platform] + when "ubuntu","debian" + if node[:platform_version].to_f < 8.04 + %w{subversion-tools libsvn-core-perl} + else + %w{subversion-tools libsvn-perl} + end + when "centos","redhat","fedora" + %w{subversion-devel subversion-perl} + end + +extra_packages.each do |pkg| + package pkg do + action :install + end +end diff --git a/sudo/attributes/sudoers.rb b/sudo/attributes/sudoers.rb new file mode 100644 index 0000000..717d8a8 --- /dev/null +++ b/sudo/attributes/sudoers.rb @@ -0,0 +1,21 @@ +# +# Cookbook Name:: sudo +# Attribute File:: sudoers +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set_unless[:authorization][:sudo][:groups] = Array.new +set_unless[:authorization][:sudo][:users] = Array.new diff --git a/sudo/metadata.json b/sudo/metadata.json new file mode 100644 index 0000000..c2de143 --- /dev/null +++ b/sudo/metadata.json @@ -0,0 +1,96 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs and configures sudo", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "sudo": "" + }, + "suggestions": { + + }, + "platforms": { + "freebsd": [ + + ], + "ubuntu": [ + + ], + "fedora": [ + + ], + "centos": [ + + ], + "debian": [ + + ], + "redhat": [ + + ] + }, + "version": "0.7.0", + "name": "sudo", + "conflicting": { + + }, + "attributes": { + "authorization\/sudoers\/groups": { + "default": "", + "type": "array", + "multiple_values": false, + "description": "Groups who are allowed sudo ALL", + "display_name": "Sudo Groups", + "recipes": [ + + ], + "required": false + }, + "authorization\/sudoers\/users": { + "default": "", + "type": "array", + "multiple_values": false, + "description": "Users who are allowed sudo ALL", + "display_name": "Sudo Users", + "recipes": [ + + ], + "required": false + }, + "authorization\/sudoers": { + "type": "hash", + "multiple_values": false, + "description": "Hash of Authorization\/Sudoers attributes", + "display_name": "Authorization Sudoers", + "recipes": [ + + ], + "required": false + }, + "authorization": { + "type": "hash", + "multiple_values": false, + "description": "Hash of Authorization attributes", + "display_name": "Authorization", + "recipes": [ + + ], + "required": false + } + }, + "providing": { + "sudo": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/sudo/metadata.rb b/sudo/metadata.rb new file mode 100644 index 0000000..88e9476 --- /dev/null +++ b/sudo/metadata.rb @@ -0,0 +1,31 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures sudo" +version "0.7" + +%w{redhat centos fedora ubuntu debian freebsd}.each do |os| + supports os +end + +attribute "authorization", + :display_name => "Authorization", + :description => "Hash of Authorization attributes", + :type => "hash" + +attribute "authorization/sudoers", + :display_name => "Authorization Sudoers", + :description => "Hash of Authorization/Sudoers attributes", + :type => "hash" + +attribute "authorization/sudoers/users", + :display_name => "Sudo Users", + :description => "Users who are allowed sudo ALL", + :type => "array", + :default => "" + +attribute "authorization/sudoers/groups", + :display_name => "Sudo Groups", + :description => "Groups who are allowed sudo ALL", + :type => "array", + :default => "" diff --git a/sudo/recipes/default.rb b/sudo/recipes/default.rb new file mode 100644 index 0000000..cbd92f1 --- /dev/null +++ b/sudo/recipes/default.rb @@ -0,0 +1,33 @@ +# +# Cookbook Name:: sudo +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "sudo" do + action :upgrade +end + +template "/etc/sudoers" do + source "sudoers.erb" + mode 0440 + owner "root" + group "root" + variables( + :sudoers_groups => node[:authorization][:sudo][:groups], + :sudoers_users => node[:authorization][:sudo][:users] + ) +end diff --git a/sudo/templates/default/sudoers.erb b/sudo/templates/default/sudoers.erb new file mode 100644 index 0000000..99ce129 --- /dev/null +++ b/sudo/templates/default/sudoers.erb @@ -0,0 +1,22 @@ +# +# /etc/sudoers +# +# Generated by Chef for <%= @node[:fqdn] %> +# + +Defaults !lecture,tty_tickets,!fqdn + +# User privilege specification +root ALL=(ALL) ALL + +<% @sudoers_users.each do |user| -%> +<%= user %> ALL=(ALL) ALL +<% end -%> + +# Members of the sysadmin group may gain root privileges +%sysadmin ALL=(ALL) ALL + +<% @sudoers_groups.each do |group| -%> +# Members of the group '<%= group %>' may gain root privileges +%<%= group %> ALL=(ALL) ALL +<% end -%> \ No newline at end of file diff --git a/sysadmin/files/default/memory_stats b/sysadmin/files/default/memory_stats new file mode 100755 index 0000000..0e8d918 --- /dev/null +++ b/sysadmin/files/default/memory_stats @@ -0,0 +1,279 @@ +#!/usr/bin/env ruby +# Adopted from Phusion Passenger - http://www.modrails.com/ +# Copyright (c) 2008, 2009 Phusion, 37signals +# +# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +# ANSI color codes +RESET = "\e[0m" +BOLD = "\e[1m" +WHITE = "\e[37m" +YELLOW = "\e[33m" +BLUE_BG = "\e[44m" + +# Container for tabular data. +class Table + def initialize(column_names) + @column_names = column_names + @rows = [] + end + + def add_row(values) + @rows << values.to_a + end + + def add_rows(list_of_rows) + list_of_rows.each do |row| + add_row(row) + end + end + + def remove_column(name) + i = @column_names.index(name) + @column_names.delete_at(i) + @rows.each do |row| + row.delete_at(i) + end + end + + def to_s(title = nil) + max_column_widths = [1] * @column_names.size + (@rows + [@column_names]).each do |row| + row.each_with_index do |value, i| + max_column_widths[i] = [value.to_s.size, max_column_widths[i]].max + end + end + + format_string = max_column_widths.map{ |i| "%#{-i}s" }.join(" ") + header = sprintf(format_string, *@column_names).rstrip << "\n" + if title + free_space = header.size - title.size - 2 + if free_space <= 0 + left_bar_size = 3 + right_bar_size = 3 + else + left_bar_size = free_space / 2 + right_bar_size = free_space - left_bar_size + end + result = "#{BLUE_BG}#{BOLD}#{YELLOW}" + result << "#{"-" * left_bar_size} #{title} #{"-" * right_bar_size}\n" + if !@rows.empty? + result << WHITE + result << header + end + else + result = header.dup + end + if @rows.empty? + result << RESET + else + result << ("-" * header.size) << "#{RESET}\n" + @rows.each do |row| + result << sprintf(format_string, *row).rstrip << "\n" + end + end + result + end +end + +class MemoryStats + class Process + attr_accessor :pid + attr_accessor :ppid + attr_accessor :threads + attr_accessor :vm_size # in KB + attr_accessor :rss # in KB + attr_accessor :name + attr_accessor :private_dirty_rss # in KB + + def vm_size_in_mb + return sprintf("%.1f MB", vm_size / 1024.0) + end + + def rss_in_mb + return sprintf("%.1f MB", rss / 1024.0) + end + + def private_dirty_rss_in_mb + if private_dirty_rss.is_a?(Numeric) + return sprintf("%.1f MB", private_dirty_rss / 1024.0) + else + return "?" + end + end + + def to_a + return [pid, ppid, threads, vm_size_in_mb, private_dirty_rss_in_mb, rss_in_mb, name] + end + end + + def start(match = 'rails') + procs = list_processes(:match => match).delete_if {|p| p.pid == ::Process.pid } + if !procs.empty? + if ::Process.uid != 0 && procs.any?{ |p| p.private_dirty_rss.nil? } + puts + puts "*** WARNING: Please run this tool as root. Otherwise the " << + "private dirty RSS of processes cannot be determined." + end + print_process_list("#{match}", procs) + else + puts "No processes found matching '#{match}'" + end + end + + # Returns a list of Process objects that match the given search criteria. + # + # # Search by executable path. + # list_processes(:exe => '/usr/sbin/apache2') + # + # # Search by executable name. + # list_processes(:name => 'ruby1.8') + # + # # Search by process name. + # list_processes(:match => 'Passenger FrameworkSpawner') + def list_processes(options) + if options[:exe] + name = options[:exe].sub(/.*\/(.*)/, '\1') + if RUBY_PLATFORM =~ /linux/ + ps = "ps -C '#{name}'" + else + ps = "ps -A" + options[:match] = Regexp.new(Regexp.escape(name)) + end + elsif options[:name] + if RUBY_PLATFORM =~ /linux/ + ps = "ps -C '#{options[:name]}'" + else + ps = "ps -A" + options[:match] = Regexp.new(" #{Regexp.escape(options[:name])}") + end + elsif options[:match] + ps = "ps -A" + else + raise ArgumentError, "Invalid options." + end + + processes = [] + case RUBY_PLATFORM + when /solaris/ + list = `#{ps} -o pid,ppid,nlwp,vsz,rss,comm`.split("\n") + threads_known = true + when /darwin/ + list = `#{ps} -w -o pid,ppid,vsz,rss,command`.split("\n") + threads_known = false + else + list = `#{ps} -w -o pid,ppid,nlwp,vsz,rss,command`.split("\n") + threads_known = true + end + list.shift + list.each do |line| + line.gsub!(/^ */, '') + line.gsub!(/ *$/, '') + + p = Process.new + if threads_known + p.pid, p.ppid, p.threads, p.vm_size, p.rss, p.name = line.split(/ +/, 6) + else + p.pid, p.ppid, p.vm_size, p.rss, p.name = line.split(/ +/, 5) + p.threads = "?" + end + p.name.sub!(/\Aruby: /, '') + p.name.sub!(/ \(ruby\)\Z/, '') + if p.name !~ /^ps/ && (!options[:match] || p.name.match(options[:match])) + # Convert some values to integer. + [:pid, :ppid, :vm_size, :rss].each do |attr| + p.send("#{attr}=", p.send(attr).to_i) + end + p.threads = p.threads.to_i if threads_known + + if platform_provides_private_dirty_rss_information? + p.private_dirty_rss = determine_private_dirty_rss(p.pid) + end + processes << p + end + end + return processes + end + +private + def platform_provides_private_dirty_rss_information? + return RUBY_PLATFORM =~ /linux/ + end + + # Returns the private dirty RSS for the given process, in KB. + def determine_private_dirty_rss(pid) + total = 0 + File.read("/proc/#{pid}/smaps").split("\n").each do |line| + line =~ /^(Private)_Dirty: +(\d+)/ + if $2 + total += $2.to_i + end + end + if total == 0 + return nil + else + return total + end + rescue Errno::EACCES, Errno::ENOENT + return nil + end + + def print_process_list(title, processes, options = {}) + table = Table.new(%w{PID PPID Threads VMSize Private Resident Name}) + table.add_rows(processes) + if options.has_key?(:show_ppid) && !options[:show_ppid] + table.remove_column('PPID') + end + if platform_provides_private_dirty_rss_information? + table.remove_column('Resident') + else + table.remove_column('Private') + end + puts table.to_s(title) + + if platform_provides_private_dirty_rss_information? + total_private_dirty_rss = 0 + some_private_dirty_rss_cannot_be_determined = false + processes.each do |p| + if p.private_dirty_rss.is_a?(Numeric) + total_private_dirty_rss += p.private_dirty_rss + else + some_private_dirty_rss_cannot_be_determined = true + end + end + puts "### Processes: #{processes.size}" + printf "### Total private dirty RSS: %.2f MB", total_private_dirty_rss / 1024.0 + if some_private_dirty_rss_cannot_be_determined + puts " (?)" + else + puts + end + end + end +end + +if !ARGV[0] + puts "Prints memory usage stats for processes whose title matches " + puts "Usage: memory_stats " + exit 1 +end + +MemoryStats.new.start(ARGV[0]) \ No newline at end of file diff --git a/sysadmin/recipes/default.rb b/sysadmin/recipes/default.rb new file mode 100755 index 0000000..c7bb776 --- /dev/null +++ b/sysadmin/recipes/default.rb @@ -0,0 +1,39 @@ +case node[:platform] +when "debian", "ubuntu" + package "policykit" + package "emacs22-nox" + require_recipe "apt" +else + package "emacs-nox" +end + +package "vim" +package "curl" +package "man-db" +package "strace" +package "host" +package "lsof" +package "gdb" +package "socat" +package "procmail" +package "zsh" +package "ack" + +directory "/u/system" do + owner "root" + group "admin" + mode 0755 +end + +directory "/u/system/bin" do + owner "root" + group "admin" + mode 0755 +end + +%w(memory_stats rotate-email-folders rotate-misc-log rotate-db-backups).each do |file| + remote_file "/usr/local/bin/#{file}" do + source file + mode 0755 + end +end diff --git a/sysctl/attributes/sysctl.rb b/sysctl/attributes/sysctl.rb new file mode 100755 index 0000000..39c86b5 --- /dev/null +++ b/sysctl/attributes/sysctl.rb @@ -0,0 +1 @@ +sysctl Mash.new unless attribute?(:sysctl) diff --git a/sysctl/recipes/default.rb b/sysctl/recipes/default.rb new file mode 100755 index 0000000..0369f1d --- /dev/null +++ b/sysctl/recipes/default.rb @@ -0,0 +1,8 @@ +service "procps" + +if node[:sysctl][:settings] + template "/etc/sysctl.d/60-custom-settings.conf" do + source "60-custom-settings.conf.erb" + notifies :restart, resources(:service => "procps") + end +end \ No newline at end of file diff --git a/sysctl/templates/default/60-custom-settings.conf.erb b/sysctl/templates/default/60-custom-settings.conf.erb new file mode 100755 index 0000000..22f59a4 --- /dev/null +++ b/sysctl/templates/default/60-custom-settings.conf.erb @@ -0,0 +1,6 @@ +# Custom kernel parameter settings +# Automatically generated by chef - local changes will be overwritten +# +<% @node[:sysctl][:settings].each do |k, v| %> +<%= k %> = <%= v %> +<% end %> \ No newline at end of file diff --git a/syslog/attributes/default.rb b/syslog/attributes/default.rb new file mode 100755 index 0000000..e3932e0 --- /dev/null +++ b/syslog/attributes/default.rb @@ -0,0 +1,2 @@ +syslog_ng Mash.new unless attribute?("syslog_ng") +syslog_ng[:root] = "/u/logs" \ No newline at end of file diff --git a/syslog/files/default/logsort b/syslog/files/default/logsort new file mode 100755 index 0000000000000000000000000000000000000000..0a5ce886ec5aa4c572a52d53c074c0962684294d GIT binary patch literal 19184 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

(-{_;K4U$(Hl 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 literal 0 HcmV?d00001 diff --git a/syslog/libraries/default.rb b/syslog/libraries/default.rb new file mode 100755 index 0000000..e5619a2 --- /dev/null +++ b/syslog/libraries/default.rb @@ -0,0 +1,3 @@ +def root + @node[:syslog_ng][:root] +end \ No newline at end of file diff --git a/syslog/metadata.json b/syslog/metadata.json new file mode 100755 index 0000000..449fa4d --- /dev/null +++ b/syslog/metadata.json @@ -0,0 +1,48 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "syslog::client": "", + "syslog::server": "", + "syslog": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + "logrotate": [ + + ] + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures syslog", + "version": "0.1.0", + "name": "syslog", + "providing": { + "syslog": [ + + ], + "syslog::client": [ + + ], + "syslog::server": [ + + ] + } +} \ No newline at end of file diff --git a/syslog/metadata.rb b/syslog/metadata.rb new file mode 100755 index 0000000..9abc646 --- /dev/null +++ b/syslog/metadata.rb @@ -0,0 +1,5 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures syslog" +version "0.1" +depends "logrotate" \ No newline at end of file diff --git a/syslog/recipes/client.rb b/syslog/recipes/client.rb new file mode 100755 index 0000000..a4a8e97 --- /dev/null +++ b/syslog/recipes/client.rb @@ -0,0 +1,10 @@ +require_recipe "syslog" + +template "/etc/syslog-ng/syslog-ng.conf" do + source "syslog-ng-client.conf.erb" + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "syslog-ng") + not_if { @node.recipes.include?("syslog::server") } +end \ No newline at end of file diff --git a/syslog/recipes/default.rb b/syslog/recipes/default.rb new file mode 100755 index 0000000..a056139 --- /dev/null +++ b/syslog/recipes/default.rb @@ -0,0 +1,8 @@ +package "syslog-ng" do + action :install +end + +service "syslog-ng" do + supports :restart => true, :reload => true + action [:enable, :start] +end \ No newline at end of file diff --git a/syslog/recipes/server.rb b/syslog/recipes/server.rb new file mode 100755 index 0000000..87689fe --- /dev/null +++ b/syslog/recipes/server.rb @@ -0,0 +1,51 @@ +require_recipe "syslog" + +template "/etc/syslog-ng/syslog-ng.conf" do + source "syslog-ng-server.conf.erb" + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "syslog-ng") + variables(:applications => node[:applications]) +end + +remote_file "/usr/local/bin/logsort" do + source "logsort" + mode 0755 +end + +if node[:applications] + node[:applications].each do |app, config| + directory node[:syslog_ng][:root] + "/#{app}" do + owner "app" + group "app" + mode 0750 + end + end + logrotate "applications" do + restart_command "/etc/init.d/syslog-ng reload 2>&1 || true" + files node[:applications].keys.collect{|name| root+"/#{name}/*.log" } + frequency "daily" + end +end + +directory node[:syslog_ng][:root] + "/syslog" do + owner "root" + group "app" + mode 0750 +end + +logrotate "syslog-remote" do + restart_command "/etc/init.d/syslog-ng reload 2>&1 || true" + files ['/u/logs/syslog/messages', "/u/logs/syslog/secure", "/u/logs/syslog/maillog", "/u/logs/syslog/cron", "/u/logs/syslog/bluepill"] +end + +node[:applications].each do |app, config| + next unless !config[:syslog_files].nil? && config[:syslog_files][:logsort] + + cron "logsort log rotation: #{app}" do + command "find /u/logs/#{app} -maxdepth 1 -type d -mtime +7 -exec rm -rf {} \\;" + hour "10" + minute "0" + end +end diff --git a/syslog/templates/default/syslog-ng-client.conf.erb b/syslog/templates/default/syslog-ng-client.conf.erb new file mode 100755 index 0000000..f743d8f --- /dev/null +++ b/syslog/templates/default/syslog-ng-client.conf.erb @@ -0,0 +1,29 @@ +# Generic syslog-ng configuration. Logs everything to noc. + +options { + chain_hostnames(off); + sync(0); + stats(43200); + log_msg_size(65534); +}; + +source src { + unix-stream("/dev/log" max-connections(256)); + internal(); + pipe("/proc/kmsg"); +}; + +destination d_loghost { tcp("192.168.1.153", port(5140)); }; +destination d_null { file("/dev/null.syslog"); }; +destination messages { file("/var/log/messages"); }; + +# Block SyslogNG message overflow warnings +filter f_syslog_overflow { program("syslog-ng") and match("length overflow"); }; +log { source(src); filter(f_syslog_overflow); destination(d_null); flags(final); }; + +# Block Postfix entries related to the loadbalancer +filter f_smtp_health { program("postfix") and match("192.168.1.153"); }; +log { source(src); filter(f_smtp_health); destination(d_null); flags(final); }; + +# Log everything through the log host so we can centralize Xen monitoring. +log { source(src); destination(d_loghost); }; diff --git a/syslog/templates/default/syslog-ng-server.conf.erb b/syslog/templates/default/syslog-ng-server.conf.erb new file mode 100755 index 0000000..45e3305 --- /dev/null +++ b/syslog/templates/default/syslog-ng-server.conf.erb @@ -0,0 +1,152 @@ +# syslog-ng configuration file. +# + +options { + time_reopen (10); + log_fifo_size (5000); + long_hostnames (off); + use_dns (yes); + use_fqdn (no); + create_dirs (no); + keep_hostname (yes); + flush_lines (1); + stats_freq (600); +}; + +source s_sys { + file ("/proc/kmsg" log_prefix("kernel: ")); + unix-stream ("/dev/log"); + internal(); +}; + +source s_remote { + udp(ip(0.0.0.0) port(514)); + tcp(ip(0.0.0.0) port(5140) max-connections(256) so_rcvbuf(8192) so_keepalive(yes)); +}; + +destination d_cons { file("/dev/console"); }; +destination d_mesg { file("/var/log/messages"); }; +destination d_auth { file("/var/log/secure"); }; +destination d_mail { file("/var/log/maillog" sync(10)); }; +destination d_spol { file("/var/log/spooler"); }; +destination d_boot { file("/var/log/boot.log"); }; +destination d_cron { file("/var/log/cron"); }; +destination d_mlal { usertty("*"); }; + +#filter f_filter1 { facility(kern); }; +filter f_filter2 { level(info..emerg) and not facility(mail,authpriv,cron); }; +filter f_filter3 { facility(authpriv); }; +filter f_filter4 { facility(mail); }; +filter f_filter5 { level(emerg); }; +filter f_filter6 { facility(uucp) or (facility(news) and level(crit..emerg)); }; +filter f_filter7 { facility(local7); }; +filter f_filter8 { facility(cron); }; +filter f_local0 { facility(local0); }; +filter f_local1 { facility(local1); }; +filter f_local2 { facility(local2); }; +filter f_local3 { facility(local3); }; +filter f_local4 { facility(local4); }; +filter f_local5 { facility(local5); }; +filter f_local6 { facility(local6); }; +filter f_local7 { facility(local7); }; + +filter f_exceptions { match("System Notifier"); }; +destination exceptions { file("/u/logs/exceptions.log", owner(app), group(app), perm(0640)); }; +log { source(s_remote); filter(f_exceptions); destination(exceptions); }; + +# filter HAProxy logs +filter f_haproxy { program("haproxy"); }; + +# filter passenger monitor logs +filter f_passenger { program("passenger"); }; +destination passenger_monitor { file("/u/logs/passenger_monitor.log", owner(app), group(app), perm(0640)); }; +log { source(s_remote); filter(f_passenger); destination(passenger_monitor); flags(final); }; + +# filter Rails apps +filter f_completed_in { match("Completed in"); }; +filter f_thumbnail_build { match("thumbnail\.build"); }; +filter f_memcache { match("MemCacheStore"); }; +filter f_uploader { match("Uploader"); }; + +<% @applications.each do |app, config| %> + +<% if config[:syslog_send_to_host] && @node[:ipaddress] != config[:syslog_send_to_host] %> +# send <%= app %> logs to another host +destination loghost_<%= app %> { tcp("<%= config[:syslog_send_to_host] %>", port(5140)); }; +log { source(s_remote); filter(f_<%= app %>); destination(loghost_<%= app %>); }; +<% end %> + +<% config[:syslog_files] ||= Mash.new %> +<% if config[:syslog_files][:haproxy] %> +destination d_<%= app %>_haproxy { file("/u/logs/<%= app %>/haproxy.log", owner(app), group(app), perm(0640)); }; +filter f_<%= app %>_haproxy { program("haproxy") and match("<%= app %>"); }; +log { source(s_remote); filter(f_local1); filter(f_haproxy); filter(f_<%= app %>_haproxy); destination(d_<%= app %>_haproxy); flags(final); }; +<% end %> + +filter f_<%= app %> { program("<%= app %>"); }; + +<% if config[:syslog_files][:completed] %> +destination <%= app %>_completed { file("/u/logs/<%= app %>/completed.log", owner(app), group(app), perm(0640)); }; +log { source(s_remote); filter(f_<%= app %>); filter(f_completed_in); destination(<%= app %>_completed); }; +<% end %> + +<% if config[:syslog_files][:thumbnails] %> +destination <%= app %>_thumbnails { file("/u/logs/<%= app %>/thumbnails.log", owner(app), group(app), perm(0640)); }; +log { source(s_remote); filter(f_<%= app %>); filter(f_thumbnail_build); destination(<%= app %>_thumbnails); }; +<% end %> + +<% if config[:syslog_files][:memcache] %> +destination <%= app %>_memcache { file("/u/logs/<%= app %>/memcache.log", owner(app), group(app), perm(0640)); }; +log { source(s_remote); filter(f_<%= app %>); filter(f_memcache); destination(<%= app %>_memcache); }; +<% end %> + +<% if config[:syslog_files][:uploader] %> +destination <%= app %>_uploader { file("/u/logs/<%= app %>/uploader.log", owner(app), group(app), perm(0640)); }; +log { source(s_remote); filter(f_<%= app %>); filter(f_uploader); destination(<%= app %>_uploader); }; +<% end %> + +<% if config[:syslog_files][:logsort] %> +destination <%= app %>_logsort { program("/u/apps/<%= app %>/shared/bin/logsort", log_fifo_size(30000)); }; +log { source(s_remote); filter(f_<%= app %>); destination(<%= app %>_logsort); flags(final); }; +<% else %> +destination <%= app %> { file("/u/logs/<%= app %>/rails.log", owner(app), group(app), perm(0640)); }; +log { source(s_remote); filter(f_<%= app %>); destination(<%= app %>); flags(final); }; +<% end %> + +<% end %> + + +# Standard system logging +log { source(s_sys); filter(f_filter2); destination(d_mesg); }; +log { source(s_sys); filter(f_filter3); destination(d_auth); }; +log { source(s_sys); filter(f_filter4); destination(d_mail); }; +log { source(s_sys); filter(f_filter5); destination(d_mlal); }; +log { source(s_sys); filter(f_filter6); destination(d_spol); }; +log { source(s_sys); filter(f_filter7); destination(d_boot); }; +log { source(s_sys); filter(f_filter8); destination(d_cron); }; + +# Bluepill Monitors +filter f_bluepill { program("bluepilld"); }; +destination d_bluepill { file("/u/logs/syslog/bluepill", owner(root), group(app), perm(0640)); }; +log { source(s_remote); filter(f_local6); filter(f_bluepill); destination(d_bluepill); flags(final); }; + +# Cisco Load Balancer +destination d_cisco { file("/u/logs/syslog/cisco", owner(root), group(app), perm(0640)); }; +log { source(s_remote); filter(f_local6); destination(d_cisco); flags(final); }; + + +# Remote system logging +destination d_mesg_remote { file("/u/logs/syslog/messages", owner(root), group(app), perm(0640)); }; +destination d_auth_remote { file("/u/logs/syslog/secure", owner(root), group(app), perm(0640)); }; +destination d_mail_remote { file("/u/logs/syslog/maillog", owner(root), group(app), perm(0640), sync(10)); }; +destination d_spol_remote { file("/u/logs/syslog/spooler", owner(root), group(app), perm(0640)); }; +destination d_boot_remote { file("/u/logs/syslog/boot.log", owner(root), group(app), perm(0640)); }; +destination d_cron_remote { file("/u/logs/syslog/cron", owner(root), group(app), perm(0640)); }; + +log { source(s_remote); filter(f_filter2); destination(d_mesg_remote); }; +log { source(s_remote); filter(f_filter3); destination(d_auth_remote); }; +log { source(s_remote); filter(f_filter4); destination(d_mail_remote); }; +log { source(s_remote); filter(f_filter6); destination(d_spol_remote); }; +log { source(s_remote); filter(f_filter7); destination(d_boot_remote); }; +log { source(s_remote); filter(f_filter8); destination(d_cron_remote); }; + diff --git a/teamspeak/metadata.json b/teamspeak/metadata.json new file mode 100644 index 0000000..8473b23 --- /dev/null +++ b/teamspeak/metadata.json @@ -0,0 +1,42 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs teamspeak and enables service", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "teamspeak": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ] + }, + "version": "0.8.0", + "name": "teamspeak", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "teamspeak": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + "php": [ + + ] + } +} \ No newline at end of file diff --git a/teamspeak/metadata.rb b/teamspeak/metadata.rb new file mode 100644 index 0000000..d7c95c2 --- /dev/null +++ b/teamspeak/metadata.rb @@ -0,0 +1,7 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs teamspeak and enables service" +version "0.8" +depends "php" +supports "ubuntu" diff --git a/teamspeak/recipes/default.rb b/teamspeak/recipes/default.rb new file mode 100644 index 0000000..12100f4 --- /dev/null +++ b/teamspeak/recipes/default.rb @@ -0,0 +1,73 @@ +# +# Author:: Joshua Timberman +# Cookbook Name:: teamspeak +# Recipe:: default +# +# Copyright 2008-2009, Joshua Timberman +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package "teamspeak-server" + +service "teamspeak-server" do + action :enable +end + +ts_server = "teamspeak.#{node[:domain]}" + +template "/etc/teamspeak-server/server.ini" do + source "server.ini.erb" + owner "teamspeak-server" + group "teamspeak-server" + mode "0600" +end + +include_recipe "php::php5" +include_recipe "apache2::mod_php5" + +directory "/srv/www/tsdisplay" do + action :create + recursive true + owner "www-data" + group "www-data" + mode "755" +end + +bash "install tsdisplay" do + cwd "/srv/www/tsdisplay" + code <<-EOH +wget http://softlayer.dl.sourceforge.net/sourceforge/tsdisplay/TeamspeakDisplay-PR3.zip +unzip TeamSpeakDisplay-PR3.zip +EOH + not_if { File.exists?("/srv/www/tsdisplay/TeamspeakDisplay-PR3.zip") } +end + +template "/srv/www/tsdisplay/demo.php" do + source "demo.php.erb" + owner "www-data" + group "www-data" + mode "644" + variables :ts_server => ts_server +end + +template "/etc/apache2/sites-available/teamspeak.conf" do + source "teamspeak.conf.erb" + owner "root" + group "root" + mode "644" + variables :virtual_host_name => ts_server, :docroot => "/srv/www/tsdisplay" +end + +apache_site "teamspeak.conf" do + enable :true +end diff --git a/teamspeak/templates/default/demo.php.erb b/teamspeak/templates/default/demo.php.erb new file mode 100644 index 0000000..3d15680 --- /dev/null +++ b/teamspeak/templates/default/demo.php.erb @@ -0,0 +1,94 @@ + + + + Teamspeak Display Demo + + + +\n"); + } +?> + + +

Demo:

+Error reporting "); + //echo("is currently on. Turn it off in live environments !

\n"); + //error_reporting(E_ALL); + //ini_set("display_errors", "1"); + //ini_set("display_startup_errors", "1"); + //ini_set("ignore_repeated_errors", "0"); + //ini_set("ignore_repeated_source", "0"); + //ini_set("report_memleaks", "1"); + //ini_set("track_errors", "1"); + //ini_set("html_errors", "1"); + //ini_set("warn_plus_overloading", "1"); + //================== END OF ERROR REPORTING CODE ====================== + + // Load the Teamspeak Display: + require("teamspeakdisplay/teamspeakdisplay.php"); + + // Get the default settings + $settings = $teamspeakDisplay->getDefaultSettings(); + + //================== BEGIN OF CONFIGURATION CODE ====================== + + // Set the teamspeak server IP or Hostname below (DO NOT INCLUDE THE + // PORT NUMBER): + $settings["serveraddress"] = "<%= @ts_server %>"; + + // If your you use another port than 8767 to connect to your teamspeak + // server using a teamspeak client, then uncomment the line below and + // set the correct teamspeak port: + //$settings["serverudpport"] = 8767; + + // If your teamspeak server uses another query port than 51234, then + // uncomment the line below and set the teamspeak query port of your + // server (look in the server.ini of your teamspeak server for this + // portnumber): + //$settings["serverqueryport"] = 51234; + + // If you want to limit the display to only one channel including it's + // players and subchannels, uncomment the following line and set the + // exact name of the channel. This feature is case-sensitive! + //$settings["limitchannel"] = ""; + + // If your teamspeak server uses another set of forbidden nickname + // characters than "()[]{}" (look in your server.ini for this setting), + // then uncomment the following line and set the correct set of + // forbidden nickname characters: + //$settings["forbiddennicknamechars"] = "()[]{}"; + + //================== END OF CONFIGURATION CODE ======================== + + // Is the script improperly configured? + if ($settings["serveraddress"] == "") { die("You need to configure this script as described inside the CONFIGURATION CODE block in " . $_SERVER["PHP_SELF"] . "
\n"); } + + // Display the Teamspeak server + $teamspeakDisplay->displayTeamspeakEx($settings); + + // Display autorefresh status and control link: + echo("
\n"); + if ($autorefresh == 0) { + echo("Autorefresh: Off (
Turn on)
\n"); + } else if ($autorefresh == 1) { + echo("Autorefresh: On (Turn off)
\n"); + } +?> +
+ Powered by Teamspeak Display
+ + \ No newline at end of file diff --git a/teamspeak/templates/default/port_teamspeak.erb b/teamspeak/templates/default/port_teamspeak.erb new file mode 100644 index 0000000..942570e --- /dev/null +++ b/teamspeak/templates/default/port_teamspeak.erb @@ -0,0 +1,4 @@ +# Teamspeak +-A FWR -p udp -m udp --dport 8767 -j ACCEPT +-A FWR -p tcp -m tcp --dport 51234 -j ACCEPT +-A FWR -p tcp -m tcp --dport 14534 -j ACCEPT \ No newline at end of file diff --git a/teamspeak/templates/default/server.ini.erb b/teamspeak/templates/default/server.ini.erb new file mode 100644 index 0000000..9c000b2 --- /dev/null +++ b/teamspeak/templates/default/server.ini.erb @@ -0,0 +1,38 @@ +[Main Config] +BoundToIp1= +ExternalIPDectection=1 +HTTPServer Port=<%= @node[:teamspeak][:http_port] %> +HTTPServer Enabled=1 +DateTimeFormat=dd-mm-yyyy hh:nn:ss +TCPQueryPort=<%= @node[:teamspeak][:query_port] %> +AllowedClientNameChars= +DisAllowedClientNameChars=()[]{} + +[debug] +MessageTypes=LMTALL +MessageDepths=LMDALL + +[WebPost] +AdminEmail=gaming@housepub.org +ISPLinkURL=na +ISPName=Private +ISPCountryNumber=0 +Enabled=1 +PostURL= +ListPublic=0 +UserAgent=teamspeak + +[log] +access_r=0 +access_u=0 +channel_registerred=0 +channel_unregisterred=0 +sa=0 +chat=0 +kick_server=0 +kick_channel=0 + +[Spam] +max_commands=10 +in_seconds=2 + diff --git a/teamspeak/templates/default/teamspeak.conf.erb b/teamspeak/templates/default/teamspeak.conf.erb new file mode 100644 index 0000000..3027e30 --- /dev/null +++ b/teamspeak/templates/default/teamspeak.conf.erb @@ -0,0 +1,30 @@ + + + DocumentRoot <%= @docroot %> + + ServerName <%= @virtual_host_name %> + ServerAlias <%= @virtual_host_name.split('.')[0] %> + + + Options FollowSymLinks + AllowOverride None + + + LogLevel info + ErrorLog /var/log/apache2/teamspeak-error.log + CustomLog /var/log/apache2/teamspeak-access.log combined + + RewriteEngine On + RewriteLog /var/log/apache2/teamspeak-rewrite.log + RewriteLogLevel 0 + + > + Options Indexes FollowSymLinks MultiViews + AllowOverride All + Order allow,deny + allow from all + + + AddOutputFilterByType DEFLATE text/html text/plain text/xml + + diff --git a/thin/attributes/thin.rb b/thin/attributes/thin.rb new file mode 100755 index 0000000..2089a82 --- /dev/null +++ b/thin/attributes/thin.rb @@ -0,0 +1,2 @@ +default.thin[:version] = "1.2.5" +default.thin[:persistent_connections] = true \ No newline at end of file diff --git a/thin/metadata.rb b/thin/metadata.rb new file mode 100755 index 0000000..404f071 --- /dev/null +++ b/thin/metadata.rb @@ -0,0 +1,7 @@ +maintainer "thin" +maintainer_email "sysadmins@37signals.com" +description "Configures thin, a Ruby web server" +version "0.1" +depends "ruby" +depends "nginx" +depends "apache2" diff --git a/thin/recipes/default.rb b/thin/recipes/default.rb new file mode 100755 index 0000000..d1e7101 --- /dev/null +++ b/thin/recipes/default.rb @@ -0,0 +1,3 @@ +gem_package "thin" do + version node[:thin][:version] +end \ No newline at end of file diff --git a/thin/templates/default/bluepill.conf.erb b/thin/templates/default/bluepill.conf.erb new file mode 100755 index 0000000..3bd2926 --- /dev/null +++ b/thin/templates/default/bluepill.conf.erb @@ -0,0 +1,36 @@ +require 'yaml' +config_path = "<%= @rails_root %>/config/thin/<%= @rails_env %>.yml" +config = YAML.load_file(config_path) +num_servers = config["servers"] ||= 1 + +Bluepill.application("<%= @name %>", :log_file => "<%= @rails_root %>/log/thin_bluepill.log") do |app| + + (0...num_servers).each do |i| + + # UNIX socket cluster use number 0 to 2 (for 3 servers) + # and tcp cluster use port number 3000 to 3002. + number = config['socket'] ? i : (config['port'] + i) + + app.process("<%= @name %>-#{number}") do |process| + process.group = "thins" + process.start_command = "<%= "#{@node[:ruby][:bin_dir]}/thin" %> start -C #{config_path} -o #{number}" + <% if @node[:thin][:persistent_connections] %> + # since we use persistent connections, we must forcefully kill these processes that would otherwise receive a QUIT for graceful killing + process.stop_command = "kill -TERM {{PID}}" + <% else %> + process.stop_command = "<%= "#{@node[:ruby][:bin_dir]}/thin" %> stop -C #{config_path} -o #{number}" + process.restart = "<%= "#{@node[:ruby][:bin_dir]}/thin" %> restart -C #{config_path} -o #{number}" + <% end %> + process.stdout = process.stderr = "<%= @rails_root %>/log/thin.log" + process.pid_file = "<%= @rails_root %>/tmp/pids/thin.#{number}.pid" + process.checks :mem_usage, :every => 10.seconds, :below => <%= @memory_limit %>.megabytes, :times => [3, 5] + process.uid = "<%= @user %>" + process.gid = "<%= @group %>" + + process.start_grace_time = 10.seconds + process.restart_grace_time = 10.seconds + + process.checks :flapping, :times => 2, :within => 30.seconds, :retry_in => 7.seconds + end + end +end \ No newline at end of file diff --git a/thrift/README.rdoc b/thrift/README.rdoc new file mode 100644 index 0000000..f4a8de9 --- /dev/null +++ b/thrift/README.rdoc @@ -0,0 +1,42 @@ += DESCRIPTION: + +Installs Thrift from source. + += REQUIREMENTS: + +== Platform: + +Only tested on Ubuntu 9.04. + +== Cookbooks: + +Opscode cookbooks: + +* build-essential +* java +* subversion +* boost + += USAGE: + +Include the Thrift recipe to install Thrift from source on your systems. + + include_recipe "thrift" + += LICENSE and AUTHOR: + + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/thrift/metadata.json b/thrift/metadata.json new file mode 100644 index 0000000..fbead44 --- /dev/null +++ b/thrift/metadata.json @@ -0,0 +1,51 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs\/Configures thrift", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "thrift": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ] + }, + "version": "0.1.0", + "name": "thrift", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "thrift": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nInstalls Thrift from source.\n\n= REQUIREMENTS:\n\n== Platform:\n\nOnly tested on Ubuntu 9.04.\n\n== Cookbooks:\n\nOpscode cookbooks:\n\n* build-essential\n* java\n* subversion\n* boost\n\n= USAGE:\n\nInclude the Thrift recipe to install Thrift from source on your systems.\n\n include_recipe \"thrift\"\n\n= LICENSE and AUTHOR:\n\n\nAuthor:: Joshua Timberman ()\nCopyright:: 2009, Opscode, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "replacing": { + + }, + "dependencies": { + "subversion": [ + + ], + "java": [ + + ], + "boost": [ + + ], + "build-essential": [ + + ] + } +} \ No newline at end of file diff --git a/thrift/metadata.rb b/thrift/metadata.rb new file mode 100644 index 0000000..99557f3 --- /dev/null +++ b/thrift/metadata.rb @@ -0,0 +1,12 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs/Configures thrift" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.1" + +supports "ubuntu" + +%w{ build-essential boost java subversion }.each do |cb| + depends cb +end diff --git a/thrift/recipes/default.rb b/thrift/recipes/default.rb new file mode 100644 index 0000000..d6cc343 --- /dev/null +++ b/thrift/recipes/default.rb @@ -0,0 +1,40 @@ +# +# Cookbook Name:: thrift +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +include_recipe "build-essential" +include_recipe "boost" +include_recipe "java" +include_recipe "subversion" + +%w{ flex bison libtool autoconf pkg-config }.each do |pkg| + package pkg +end + +bash "install_thrift" do + user "root" + cwd "/tmp" + code <<-EOH + svn co http://svn.apache.org/repos/asf/incubator/thrift thrift + cd thrift/trunk; + cp /usr/share/aclocal/pkg.m4 ./aclocal + sh bootstrap.sh + ./configure --with-boost=/usr/local --with-libevent=/usr/local --prefix=/usr/local + make install + EOH + not_if { FileTest.exists?("/usr/local/bin/thrift") } +end diff --git a/timezone/attributes/timezone.rb b/timezone/attributes/timezone.rb new file mode 100755 index 0000000..76140e2 --- /dev/null +++ b/timezone/attributes/timezone.rb @@ -0,0 +1,2 @@ +timezone Mash.new unless attribute?("timezone") +timezone "UTC" \ No newline at end of file diff --git a/timezone/metadata.json b/timezone/metadata.json new file mode 100755 index 0000000..095a65b --- /dev/null +++ b/timezone/metadata.json @@ -0,0 +1,38 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "timezone": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures timezone", + "version": "0.1.0", + "name": "timezone", + "providing": { + "timezone": [ + + ] + } +} \ No newline at end of file diff --git a/timezone/metadata.rb b/timezone/metadata.rb new file mode 100755 index 0000000..7c7d949 --- /dev/null +++ b/timezone/metadata.rb @@ -0,0 +1,4 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures timezone" +version "0.1" diff --git a/timezone/recipes/default.rb b/timezone/recipes/default.rb new file mode 100755 index 0000000..b3765fb --- /dev/null +++ b/timezone/recipes/default.rb @@ -0,0 +1,5 @@ +link "/etc/localtime" do + filename = "/usr/share/zoneinfo/#{node[:timezone]}" + to filename + only_if { File.exists? filename } +end \ No newline at end of file diff --git a/tomcat/attributes/default.rb b/tomcat/attributes/default.rb new file mode 100755 index 0000000..301cf52 --- /dev/null +++ b/tomcat/attributes/default.rb @@ -0,0 +1,6 @@ +tomcat Mash.new unless attribute?(:tomcat) +tomcat[:user] = "tomcat6" unless tomcat.has_key?(:user) +tomcat[:port] = "8080" unless tomcat.has_key?(:port) +tomcat[:java_home] = "/usr/lib/jvm/java-6-sun" unless tomcat.has_key?(:java_home) +tomcat[:heap_size] = "128M" unless tomcat.has_key?(:heap_size) +tomcat[:stack_size] = "16M" unless tomcat.has_key?(:stack_size) diff --git a/tomcat/metadata.json b/tomcat/metadata.json new file mode 100755 index 0000000..8cef216 --- /dev/null +++ b/tomcat/metadata.json @@ -0,0 +1,40 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "tomcat": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + "java": [ + + ] + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures tomcat", + "version": "0.1.0", + "name": "tomcat", + "providing": { + "tomcat": [ + + ] + } +} \ No newline at end of file diff --git a/tomcat/metadata.rb b/tomcat/metadata.rb new file mode 100755 index 0000000..6dd914c --- /dev/null +++ b/tomcat/metadata.rb @@ -0,0 +1,5 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures tomcat" +version "0.1" +depends "java" diff --git a/tomcat/recipes/default.rb b/tomcat/recipes/default.rb new file mode 100755 index 0000000..98ba463 --- /dev/null +++ b/tomcat/recipes/default.rb @@ -0,0 +1,29 @@ +require_recipe "java" + +return unless ["ubuntu", "debian"].include?(node[:platform]) + +package "tomcat6" +service "tomcat6" + +template "/etc/default/tomcat6" do + source "default.tomcat6.erb" + owner "root" + group "root" + mode 0644 + + notifies :restart, resources(:service => "tomcat6") +end + +template "/etc/tomcat6/server.xml" do + source "server.xml.erb" + owner "app" + group "admin" + mode 0644 + + notifies :restart, resources(:service => "tomcat6") +end + +execute "fix_permissions" do + command "chown -R #{node[:tomcat][:user]}:admin /etc/tomcat6 /var/log/tomcat6 /var/lib/tomcat6 /var/cache/tomcat6 && touch /etc/tomcat6/perms.ok" + creates "/etc/tomcat6/perms.ok" +end diff --git a/tomcat/templates/default/default.tomcat6.erb b/tomcat/templates/default/default.tomcat6.erb new file mode 100755 index 0000000..ad7f737 --- /dev/null +++ b/tomcat/templates/default/default.tomcat6.erb @@ -0,0 +1,30 @@ +# Run Tomcat as this user ID. Not setting this or leaving it blank will use the +# default of tomcat6. +TOMCAT6_USER=<%= @node[:tomcat][:user] %> + +# The home directory of the Java development kit (JDK). You need at least +# JDK version 1.5. If JAVA_HOME is not set, some common directories for +# OpenJDK, the Sun JDK, and various J2SE 1.5 versions are tried. +<% if @node[:tomcat].has_key?(:java_home) -%> +JAVA_HOME=<%= @node[:tomcat][:java_home] %> +<% end -%> + +# Directory for per-instance configuration files and webapps. It contains the +# directories conf, logs, webapps, work and temp. See RUNNING.txt for details. +# Default: /var/lib/tomcat6 +#CATALINA_BASE=/var/lib/tomcat6 + +# Arguments to pass to the Java virtual machine (JVM). +JAVA_OPTS="-Djava.awt.headless=true -Xmx<%= @node[:tomcat][:heap_size] %> -Xss<%= @node[:tomcat][:stack_size] %>" + +# Java compiler to use for translating JavaServer Pages (JSPs). You can use all +# compilers that are accepted by Ant's build.compiler property. +#JSP_COMPILER=jikes + +# Use the Java security manager? (yes/no, default: yes) +# WARNING: Do not disable the security manager unless you understand +# the consequences! +TOMCAT6_SECURITY=no + +# Number of days to keep logfiles in /var/log/tomcat6. Default is 14 days. +#LOGFILE_DAYS=14 diff --git a/tomcat/templates/default/server.xml.erb b/tomcat/templates/default/server.xml.erb new file mode 100755 index 0000000..455d6ef --- /dev/null +++ b/tomcat/templates/default/server.xml.erb @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + diff --git a/tomcat6/README.rdoc b/tomcat6/README.rdoc new file mode 100644 index 0000000..50a65ef --- /dev/null +++ b/tomcat6/README.rdoc @@ -0,0 +1,38 @@ += DESCRIPTION: + +Installs Tomcat6 + += REQUIREMENTS: + +== Platform and Application Environment: + +Tested on Centos 5.2 8.10. May work on other platforms, esp Redhat. +Needs Java at least Java 5 + +== Cookbooks: + +Opscode cookbooks, http://github.com/opscode/cookbooks/tree/master: + +* java + += ATTRIBUTES: + += USAGE: + + += LICENSE and AUTHOR: + +Author:: Edmund Haselwanter () +Copyright:: 2009, Edmund Haselwanter + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/tomcat6/attributes/tomcat6.rb b/tomcat6/attributes/tomcat6.rb new file mode 100644 index 0000000..a656490 --- /dev/null +++ b/tomcat6/attributes/tomcat6.rb @@ -0,0 +1,41 @@ +# Where the various parts of tomcat6 are +case platform +when "centos" + set[:tomcat6][:start] = "/etc/init.d/tomcat6 start" + set[:tomcat6][:stop] = "/etc/init.d/tomcat6 stop" + set[:tomcat6][:restart] = "/etc/init.d/tomcat6 restart" + set[:tomcat6][:home] = "/usr/share/tomcat6" #don't use trailing slash. it breaks init script + set[:tomcat6][:dir] = "/etc/tomcat6/" + set[:tomcat6][:conf] = "/etc/tomcat6" + set[:tomcat6][:temp] = "/var/tmp/tomcat6" + set[:tomcat6][:logs] = "/var/log/tomcat6" + set[:tomcat6][:webapp_base_dir] = "/srv/tomcat6/" + set[:tomcat6][:webapps] = File.join(tomcat6[:webapp_base_dir],"webapps") + set[:tomcat6][:user] = "tomcat" + set[:tomcat6][:manager_dir] = File.join(tomcat6[:home],"webapps/manager") + set[:tomcat6][:port] = 8080 + set[:tomcat6][:ssl_port] = 8433 +else + set[:tomcat6][:start] = "/etc/init.d/tomcat6 start" + set[:tomcat6][:stop] = "/etc/init.d/tomcat6 stop" + set[:tomcat6][:restart] = "/etc/init.d/tomcat6 restart" + set[:tomcat6][:home] = "/usr/share/tomcat6" #don't use trailing slash. it breaks init script + set[:tomcat6][:dir] = "/etc/tomcat" + set[:tomcat6][:conf] = "/etc/tomcat6" + set[:tomcat6][:temp] = "/var/tmp/tomcat6" + set[:tomcat6][:logs] = "/var/log/tomcat6" + set[:tomcat6][:webapp_base_dir] = "/srv/tomcat6/" + set[:tomcat6][:webapps] = File.join(tomcat6[:webapp_base_dir],"webapps") + set[:tomcat6][:user] = "tomcat" + set[:tomcat6][:manager_dir] = "/usr/share/tomcat6/webapps/manager" + set[:tomcat6][:port] = 8080 + set[:tomcat6][:ssl_port] = 8433 +end + +set_unless[:tomcat6][:version] = "6.0.18" +set_unless[:tomcat6][:with_native] = false +set_unless[:tomcat6][:java_home] = "/usr/lib/jvm/java" +set_unless[:tomcat6][:java_opts] = "" +set_unless[:tomcat6][:manager_user] = "manager" +set_unless[:tomcat6][:manager_password] = "manager" +set_unless[:tomcat6][:permgen_min_free_in_mb] = 24 diff --git a/tomcat6/definitions/tomcat_app.rb b/tomcat6/definitions/tomcat_app.rb new file mode 100644 index 0000000..139597f --- /dev/null +++ b/tomcat6/definitions/tomcat_app.rb @@ -0,0 +1,2 @@ + + diff --git a/tomcat6/files/centos/rightscale.repo b/tomcat6/files/centos/rightscale.repo new file mode 100644 index 0000000..c17ba19 --- /dev/null +++ b/tomcat6/files/centos/rightscale.repo @@ -0,0 +1,15 @@ +[rightscale] +name=RightScale Packages for Enterprise Linux 5 +baseurl=http://mirror.rightscale.com/rightscale_software/centos/5/i386 + http://mirror1.rightscale.com/rightscale_software/centos/5/i386 + http://mirror2.rightscale.com/rightscale_software/centos/5/i386 + http://mirror1-int.rightscale.com/rightscale_software/centos/5/i386 + http://mirror2-int.rightscale.com/rightscale_software/centos/5/i386 + http://mirror3-int.rightscale.com/rightscale_software/centos/5/i386 +failovermethod=priority +enabled=1 +gpgcheck=0 +gpgkey= +# set metadata to expire faster then main +metadata_expire=30 + diff --git a/tomcat6/files/default/JVM-MANAGEMENT-MIB.mib b/tomcat6/files/default/JVM-MANAGEMENT-MIB.mib new file mode 100644 index 0000000..87c27bf --- /dev/null +++ b/tomcat6/files/default/JVM-MANAGEMENT-MIB.mib @@ -0,0 +1,3246 @@ +-- +-- @(#)JVM-MANAGEMENT-MIB.mib 1.32 04/07/16 +-- +-- Copyright 2004 Sun Microsystems, Inc. All rights reserved. +-- This software is the proprietary information of Sun Microsystems, Inc. +-- Use is subject to license terms. +-- +-- The JVM-MANAGEMENT-MIB Module +-- +-- See jvmManagementMIB MODULE-IDENTITY for a description overview. +-- See conformance statements for mandatory objects +-- + +JVM-MANAGEMENT-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, Counter32, Gauge32, + Integer32, Counter64, enterprises + FROM SNMPv2-SMI + DisplayString, TEXTUAL-CONVENTION, RowPointer + FROM SNMPv2-TC + MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP + FROM SNMPv2-CONF; + +-- Module Identity +------------------ + +jvmMgtMIB MODULE-IDENTITY + LAST-UPDATED "200403041800Z" + -- Format is "YYYYMMDDhhmmZ" + ORGANIZATION "Sun Microsystems, Inc." + CONTACT-INFO "Sun Microsystems, Inc. + 4150 Network Circle + Santa Clara, CA 95054 + 1-800-555-9SUN or + 1-650-960-1300 + http://www.sun.com + or contact your local support representative" + DESCRIPTION + "Copyright 2004 Sun Microsystems, Inc. All rights reserved. + + This module defines the MIB that provides access to the + Java[tm] Virtual Machine monitoring data. + This module is derived from the Java[tm] programming language APIs + described in the java.lang.management package of + Java[tm] 2, Standard Edition, 5.0. + + See the Java programming language APIs of JSR 163 for + 'Monitoring and Management of the Java[TM] Virtual Machine' + for more details. + + Where the Java programming language API uses long, or int, + the MIB often uses the corresponding unsigned quantity - + which is closer to the object semantics. + + In those cases, it often happens that the -1 value that might + be used by the API to indicate an unknown/unimplemented + value cannot be used. Instead the MIB uses the value 0, which + stricly speaking cannot be distinguished from a valid value. + In many cases however, a running system will have non-zero + values, so using 0 instead of -1 to indicate an unknown + quantity does not lose any functionality. + " + REVISION "200403041800Z" + -- Format is "YYYYMMDDhhmmZ" + DESCRIPTION + " + JVM-MANAGEMENT-MIB - JSR 163 Final Release 1.0 + " + + ::= { standard jsr163(163) 1 } + + +-- Enterprise OIDs +------------------ + +-- internet OBJECT IDENTIFIER ::= { iso(1) org(3) dod(6) 1 } +-- private OBJECT IDENTIFIER ::= { internet 4 } +-- enterprises OBJECT IDENTIFIER ::= { private 1 } + sun OBJECT IDENTIFIER ::= { enterprises 42 } + jmgt OBJECT IDENTIFIER ::= { sun products(2) 145 } + -- experimental OBJECT IDENTIFIER ::= { jmgt 1 } + standard OBJECT IDENTIFIER ::= { jmgt 3 } + +---------------------------------------------------------------------------- +-- Textual Conventions +---------------------- +-- +-- Note: Some of the TEXTUAL-CONVENTIONs defined in this module are +-- OCTET STRING with a 1023 size limitation (SIZE(0..1023)). +-- +-- As per RFC2578, section 7.1.2. OCTET STRING: +-- +-- "The OCTET STRING type represents arbitrary binary or textual data. +-- Although the SMI-specified size limitation for this type is 65535 +-- octets, MIB designers should realize that there may be +-- implementation and interoperability limitations for sizes in +-- excess of 255 octets." +-- +-- As a consequence an agent implementing this MIB may decide to +-- restrict this maximum size to a lesser value than 1023, provided that +-- it makes it clear in an AGENT-CAPABILITY statement. +-- +---------------------------------------------------------------------------- + +JvmUnsigned64TC ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "A non-negative 64-bit bit integer, without counter + semantics." + -- We have cloned the Unsigned64TC defined in RFC 2564 rather + -- than importing it because the JVM-MANAGEMENT-MIB and the + -- APPLICATION-MIB are not related. + -- + REFERENCE "RFC 2564 - APPLICATION-MIB, Unsigned64TC." + SYNTAX Counter64 + + +JvmJavaObjectNameTC ::= TEXTUAL-CONVENTION + DISPLAY-HINT "255a" + STATUS current + DESCRIPTION + "An Object Name, as implemented by the java.lang.management API, + which identify a runtime Object (e.g. a Class Loader, a + Memory Manager, etc...). + The name is assumed to be unique in the scope of the object's + class. + + This object syntax is equivalent to a DisplayString, but with a + a 1023 bytes size limits (instead of 255 for a DisplayString). + + Note that the SNMP agent may have to truncate the string returned + by the underlying API if it does not fit in this type. + (1023 bytes max). + " + SYNTAX OCTET STRING (SIZE (0..1023)) + +JvmPathElementTC ::= TEXTUAL-CONVENTION + DISPLAY-HINT "255a" + STATUS current + DESCRIPTION + "A file or directory element in a PATH/CLASSPATH/LIBRARY_PATH + structure. + + This object syntax is equivalent to a DisplayString, but with a + a 1023 bytes size limits (instead of 255 for a DisplayString). + + Note that the SNMP agent may have to truncate the string returned + by the underlying API if it does not fit in this type. + (1023 bytes max). + " + SYNTAX OCTET STRING (SIZE (0..1023)) + +JvmArgValueTC ::= TEXTUAL-CONVENTION + DISPLAY-HINT "255a" + STATUS current + DESCRIPTION + "A string representing an input argument. + + This object syntax is equivalent to a DisplayString, but with a + a 1023 bytes size limits (instead of 255 for a DisplayString). + + Note that the SNMP agent may have to truncate the string returned + by the underlying API if it does not fit in this type. + (1023 bytes max). + " + SYNTAX OCTET STRING (SIZE (0..1023)) + +JvmVerboseLevelTC ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Defines whether the verbose flag for a feature is active. + verbose: the flag is on. + silent: the flag is off. + " + SYNTAX INTEGER { silent(1), verbose(2) } + + +JvmImplSupportStateTC ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Defines whether a feature is supported or not. + " + SYNTAX INTEGER { unsupported(1), supported(2) } + +JvmImplOptFeatureStateTC ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Defines whether an optional feature is supported, enabled, + or disabled. + + An optional feature can be: + + unsupported: The JVM does not support this feature. + enabled : The JVM supports this feature, and it + is enabled. + disabled : The JVM supports this feature, and it + is disabled. + + Only enabled(3) and disabled(4) may be supplied as values to a + SET request. unsupported(1) can only be set internally by the + agent. + " + SYNTAX INTEGER { unsupported(1), enabled(3), disabled(4) } + +JvmTimeMillis64TC ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "An elapsed time, expressed in milli-seconds. + This type is based on Counter64, but without its specific + semantics. + " + SYNTAX Counter64 + +JvmTimeNanos64TC ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "An elapsed time, expressed in nano-seconds. + This type is based on Counter64, but without its specific + semantics. + " + SYNTAX Counter64 + +JvmPositive32TC ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "A positive Integer32. In Java that would be a number + in [0..Integer.MAX_VALUE]. + " + -- We use Integer32 (0..2147483647) rather than Unsigned32 because + -- Unsigned32 (0..2147483647) because Unsigned32 is based on + -- Gauge32 - which has a specific ASN.1 tag and a specific semantics. + -- In principle you cannot use a Gauge32 as base type for an index + -- in a table. + -- Note also that Unsigned32 is (0..2^32-1) + -- while Positive32 is (0..2^31-1) + -- + SYNTAX Integer32 (0..2147483647) + +JvmManagedMemoryTypeTC ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + " + Defines the type of memory contained in a memory pool. + The pool may contain, heap memory or non-heap memory. + " + SYNTAX INTEGER { nonheap(1), heap(2) } + + +JvmValidityStateTC ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + " + Defines whether an object is still valid. + " + SYNTAX INTEGER { invalid(1), valid(2) } + + +JvmThreadStateTC ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Defines the possible states of a thread running in the + Java virtual machine. They are virtual machine thread states + and do not reflect any operating system thread states. + + The first two bits: inNative(1) and suspended(2) can be + combined together and with any other bits. The remaining + bits 3-9, are mutually exclusive. Bits 10-16 are reserved + for future evolution of this MIB. + + An agent MUST always return a thread state with one of the + bits in the range 3-9 set to 1. The other(9) bit should only + be set to 1 if new thread states which are mutally exclusive + with bits 3-8 are defined. An implementation can define + additional implementation dependant states and uses bits + from bit 17. + + See java.lang.Thread.State, + java.lang.management.ThreadInfo. + " + -- + -- Take care that in SNMP bits are numbered starting at 1, from + -- left to right (1 is the highest bit). A bitmap defined by the + -- BITS construct is thus a byte array where bit 1 is the highest bit + -- of the first byte. + -- + SYNTAX BITS { -- Bits 1-2 may be specified in any combination + inNative(1), + suspended(2), + + -- Bits 3-9 are mutually exclusive. Attempting to + -- set more than a single bit to 1 will result in + -- a returned error-status of inconsistentValue. + newThread(3), + runnable(4), + blocked(5), + terminated(6), + waiting(7), + timedWaiting(8), + other(9) + -- Bits 10-16 are reserved for future use by + -- this MIB + } + + +JvmIndex64TC ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "A 64 bits string mapping an unsigned 64 bits integer value + in big-endian ordering (i.e: 1 is encoded as 0x0000000000000001). + + This type can be used when an unsigned 64 bits integer needs + to be used inside a table index. + " + SYNTAX OCTET STRING (SIZE(8)) + + +-- OBJECT-TYPE OID tree +----------------------- + +jvmMgtMIBObjects + OBJECT IDENTIFIER ::= { jvmMgtMIB 1 } +jvmMgtMIBNotifications + OBJECT IDENTIFIER ::= { jvmMgtMIB 2 } +jvmMgtMIBConformance + OBJECT IDENTIFIER ::= { jvmMgtMIB 3 } + +----------------------------------------------------------------------- +-- +-- The JVM Class Loading group +-- +-- A collection of objects used to monitor Class Loading in the +-- Java Virtual Machine. These objects define the SNMP management +-- interface for the class loading system of the Java virtual machine. +-- +-- This group only contains a few scalar object and no tables. The objects +-- from this group are mapped from the java.lang.management.ClassLoadingMXBean +-- interface. +-- +-- See J2SE 5.0 API Specification, +-- java.lang.management.ClassLoadingMXBean +----------------------------------------------------------------------- + +-- Root OBJECT IDENTIFIER for ClassLoading group. +-- +jvmClassLoading OBJECT IDENTIFIER ::= { jvmMgtMIBObjects 1 } + +-- The following objects are mapped from the ClassLoadingMXBean interface. +----------------------------------------------------------------------- + +jvmClassesLoadedCount OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of classes currently loaded in the JVM. + + See java.lang.management.ClassLoadingMXBean.getLoadedClassCount() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ClassLoadingMXBean" + ::= { jvmClassLoading 1 } + +jvmClassesTotalLoadedCount OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of classes that have been loaded since + the JVM has started execution. + + See java.lang.management.ClassLoadingMXBean. + getTotalLoadedClassCount() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ClassLoadingMXBean" + ::= { jvmClassLoading 2 } + +jvmClassesUnloadedCount OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of classes that have been unloaded since + the JVM has started execution. + + See java.lang.management.ClassLoadingMXBean.getUnloadedClassCount() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ClassLoadingMXBean" + ::= { jvmClassLoading 3 } + +jvmClassesVerboseLevel OBJECT-TYPE + SYNTAX JvmVerboseLevelTC + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Enables or disables the verbose output for the class loading + system. The verbose output information and the output stream + to which the verbose information is emitted are implementation + dependent. Typically, a Java virtual machine implementation + prints a message each time a class file is loaded. + + verbose: if the verbose output is enabled. + silent: otherwise. + + See java.lang.management.ClassLoadingMXBean.isVerbose(), + java.lang.management.ClassLoadingMXBean.setVerbose() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ClassLoadingMXBean" + DEFVAL { silent } + ::= { jvmClassLoading 4 } + + +----------------------------------------------------------------------- +-- +-- The JVM Memory group +-- +-- A collection of objects used to monitor memory management in the +-- Java Virtual Machine. These objects define management interface for +-- the memory system of the Java virtual machine. +-- +-- Memory: +-- +-- The memory system of the Java virtual machine manages the following +-- kinds of memory: heap, and non-heap. More information on these types +-- of memory can be obtained from the J2SE 5.0 API Specification, +-- java.lang.management.MemoryMXBean. +-- +-- Memory Pools and Memory Managers: +-- +-- Memory pools and memory managers are the abstract entities that monitor +-- and manage the memory system of the Java virtual machine. +-- +-- Memory managers are represented by the jvmMemManagerTable, which contains +-- one row per Memory manager. +-- The garbage collector is one type of memory manager responsible for +-- reclaiming memory occupied by unreachable objects. +-- The jvmMemGCTable is an extension of the jvmMemManagerTable, which contains +-- the attribute specific to garbage collectors. A garbage collector entity +-- is thus represented by one row in the jvmMemManagerTable, and one +-- extension row in the jvmMemGCTable. +-- +-- Memory Pools are represented by the jvmMemPoolTable, which contains one +-- row per memory pool. A Java virtual machine may create or remove +-- memory pools during execution. A memory pool can belong to either the +-- heap or the non-heap memory. +-- +-- A memory manager is responsible for managing one or more memory pools. +-- A memory pool can be managed by more than one memory manager. +-- The jvmMemMgrRelPoolTable represents this managing/managed relationship. +-- +-- A Java virtual machine may add or remove memory managers during execution. +-- +-- See J2SE 5.0 API Specification, java.lang.management.MemoryMXBean for +-- more information on memory types, memory managers, memory pools, +-- and the memory subsystem. +-- +----------------------------------------------------------------------- + +-- Root OBJECT IDENTIFIER for the JVM Memory group. +-- +jvmMemory OBJECT IDENTIFIER ::= { jvmMgtMIBObjects 2 } + +-- The following objects are mapped from the MemoryMXBean interface. +----------------------------------------------------------------------- + +jvmMemoryPendingFinalCount OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The approximate number objects that are pending for finalization. + + See java.lang.management.MemoryMXBean. + getObjectPendingFinalizationCount() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryMXBean" + ::= { jvmMemory 1 } + +jvmMemoryGCVerboseLevel OBJECT-TYPE + SYNTAX JvmVerboseLevelTC + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Enables or disables verbose output for the memory system. + The verbose output information and the output stream to which + the verbose information is emitted are implementation dependent. + Typically, a Java virtual machine implementation prints a + message whenever it frees memory at garbage collection. + + verbose: if the verbose output is enabled, + silent: otherwise. + + See java.lang.management.MemoryMXBean.isVerbose(), + java.lang.management.MemoryMXBean.setVerbose() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryMXBean" + ::= { jvmMemory 2 } + +jvmMemoryGCCall OBJECT-TYPE + SYNTAX INTEGER { unsupported(1), supported(2), start(3), + started(4), failed(5) } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "This object makes it possible to remotelly trigger the + Garbage Collector in the JVM. + + This object's syntax is an enumeration which defines: + + * Two state values, that can be returned from a GET request: + + unsupported(1): means that remote invocation of gc() is not + supported by the SNMP agent. + supported(2) : means that remote invocation of gc() is supported + by the SNMP agent. + + * One action value, that can be provided in a SET request to + trigger the garbage collector: + + start(3) : means that a manager wishes to trigger + garbage collection. + + * Two result value, that will be returned in the response to a + SET request when remote invocation of gc is supported + by the SNMP agent: + + started(4) : means that garbage collection was + successfully triggered. It does not mean + however that the action was successfullly + completed: gc might still be running when + this value is returned. + failed(5) : means that garbage collection couldn't be + triggered. + + * If remote invocation is not supported by the SNMP agent, then + unsupported(1) will always be returned as a result of either + a GET request, or a SET request with start(3) as input value. + + * If a SET request with anything but start(3) is received, then + the agent will return a wrongValue error. + + See java.lang.management.MemoryMXBean.gc() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryMXBean" + ::= { jvmMemory 3 } + +-- The object identifiers in the range jvmMemory.[4-9] are reserved for future +-- evolution of this MIB. +-- +-- We use the range jvmMemory.[10..19] for objects related to global JVM +-- heap memory usage, as returned by +-- java.lang.management.MemoryMXBean.getHeapMemoryUsage(). +-- Object identifiers in the range jvmMemory.[14..19] are not used but +-- reserved for future evolution of this MIB. +-- +jvmMemoryHeapInitSize OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + Total amount of memory (in bytes) that the Java virtual machine + initially requests from the operating system for memory management + for heap memory pools. + + See java.lang.management.MemoryMXBean.getHeapMemoryUsage().getInit() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryMXBean, + java.lang.management.MemoryUsage" + ::= { jvmMemory 10 } + + +jvmMemoryHeapUsed OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + Total amount of used memory (in bytes) from heap memory pools. + + See java.lang.management.MemoryMXBean.getHeapMemoryUsage().getUsed() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryMXBean, + java.lang.management.MemoryUsage" + ::= { jvmMemory 11 } + +jvmMemoryHeapCommitted OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + Total amount of memory (in bytes) committed by heap memory pools. + + See java.lang.management.MemoryMXBean.getHeapMemoryUsage(). + getCommitted() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryMXBean, + java.lang.management.MemoryUsage" + ::= { jvmMemory 12 } + +jvmMemoryHeapMaxSize OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + Total maximum size of memory (in bytes) for all heap memory pools. + + See java.lang.management.MemoryMXBean.getHeapMemoryUsage().getMax() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryMXBean, + java.lang.management.MemoryUsage" + ::= { jvmMemory 13 } + +-- We use the range jvmMemory.[20..29] for objects related to global JVM +-- heap memory usage, as returned by +-- lang.management.MemoryMXBean.getNonHeapMemoryUsage(). +-- Object identifiers in the range jvmMemory.[24..29] are not used but are +-- reserved for future evolution of this MIB. +-- +jvmMemoryNonHeapInitSize OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + Total amount of memory (in bytes) that the Java virtual machine + initially requests from the operating system for memory management + for non heap memory pools. + + See java.lang.management.MemoryMXBean.getNonHeapMemoryUsage().getInit() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryMXBean, + java.lang.management.MemoryUsage" + ::= { jvmMemory 20 } + + +jvmMemoryNonHeapUsed OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + Total amount of used memory (in bytes) from non heap memory pools. + + See java.lang.management.MemoryMXBean.getNonHeapMemoryUsage().getUsed() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryMXBean, + java.lang.management.MemoryUsage" + ::= { jvmMemory 21 } + +jvmMemoryNonHeapCommitted OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + Total amount of memory (in bytes) committed by non heap memory pools. + + See java.lang.management.MemoryMXBean. + getNonHeapMemoryUsage().getCommitted() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryMXBean, + java.lang.management.MemoryUsage" + ::= { jvmMemory 22 } + +jvmMemoryNonHeapMaxSize OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + Total maximum size of memory (in bytes) for all non heap memory pools. + + See java.lang.management.MemoryMXBean.getNonHeapMemoryUsage().getMax() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryMXBean, + java.lang.management.MemoryUsage" + ::= { jvmMemory 23 } + +-- The object identifiers in the range jvmMemory.[30-99] are not used but are +-- reserved for future evolution of this MIB. +-- +-- The JVM Memory Manager Table +-- +-- The jvmMemManagerTable represent memory manager abstract entities. +-- The jvmMemManagerTable contains one row per memory manager. In +-- addition, those memory managers which are also garbage collectors have +-- an extension row in the jvmMemGCTable. +-- +-- See J2SE 5.0 API Specification, java.lang.management.MemoryMXBean for +-- a detailed description of the memory subsystem. +-- +-- See J2SE 5.0 API Specification, java.lang.management.MemoryManagerMXBean +-- for more information on memory managers. +-- +----------------------------------------------------------------------- +-- +-- We use the range jvmMemory.[100..109] for objects related to memory +-- managers. +-- Object identifiers in the range jvmMemory.[102-109] are not used +-- but are reserved for future evolution of this MIB. +-- +jvmMemManagerTable OBJECT-TYPE + SYNTAX SEQUENCE OF JvmMemManagerEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Memory Manager Table contains the whole list of Memory + Managers as returned by ManagementFactory.getMemoryManagerMXBeans(). + + When a MemoryManagerMXBean object is an instance of + GarbageCollectorMXBean, then additional information specific to + the GarbageCollectorMXBean class will be found in the + jvmGCTable, at the same index. + + Relationships between MemoryManagers and MemoryPools are shown + by the Memory Manager-Pool Relation table (jvmMemMgrPoolRelTable). + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryManagerMXBean" + ::= { jvmMemory 100 } + +jvmMemManagerEntry OBJECT-TYPE + SYNTAX JvmMemManagerEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A jvmMemManagerEntry conceptual row represent an instance of the + java.lang.management.MemoryManagerMXBean interface. If that instance + is also an instance of java.lang.management.GarbageCollectorMXBean, + then additional information will be found in the jvmGCTable, at the + same index. + + Columnar objects in this table are mapped from attributes of + the MemoryManagerMXBean interface. + + See java.lang.management.MemoryManagerMXBean + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryManagerMXBean" + INDEX { jvmMemManagerIndex } + ::= { jvmMemManagerTable 1 } + +JvmMemManagerEntry ::= SEQUENCE { + jvmMemManagerIndex JvmPositive32TC, + jvmMemManagerName JvmJavaObjectNameTC, + jvmMemManagerState JvmValidityStateTC +} + +jvmMemManagerIndex OBJECT-TYPE + SYNTAX JvmPositive32TC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An index opaquely computed by the agent and which uniquely + identifies a Memory Manager. + + The jvmMemManagerIndex index is opaquely computed by the agent, + from e.g the hash code of the MemoryManager (or MemoryManager name). + The agent is responsible for allocating a free index when it needs + one (e.g. if two objects have the same hash, then it may increment + one of the values until the conflict is resolved). As a result a + manager must not depend on the value of that index across, + e.g. reboot of the agent, as this value is not guaranteed to + stay identical after the agent restarts. + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryManagerMXBean" + ::= { jvmMemManagerEntry 1 } + +jvmMemManagerName OBJECT-TYPE + SYNTAX JvmJavaObjectNameTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The name of this memory manager, as returned by + MemoryManagerMXBean.getName(). + + See java.mangement.MemoryManagerMXBean.getName(). + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryManagerMXBean" + ::= { jvmMemManagerEntry 2 } + +jvmMemManagerState OBJECT-TYPE + SYNTAX JvmValidityStateTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + Indicates whether this memory manager is valid in the Java + virtual machine. A memory manager becomes invalid once the + Java virtual machine removes it from the memory system. + + See java.lang.management.MemoryManagerMXBean.isValid() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryManagerMXBean" + ::= { jvmMemManagerEntry 3 } + + +-- The JVM Garbage Collector Table +-- +-- The jvmMemGCTable is an extension of the jvmMemManagerTable. +-- It represents garbage collector abstract entities. A garbage collector +-- is a memory manager responsible for reclaiming memory occupied by +-- unreachable objects. +-- +-- A garbage collector is thus represented by one row in the +-- jvmMemManagerTable, plus an extension row in the jvmMemGCTable. +-- The extension row in the jvmMemGCTable contains those attributes which +-- are specific to garbage collectors. +-- +-- See J2SE 5.0 API Specification, java.lang.management.MemoryMXBean for +-- a detailed description of the memory subsystem. +-- +-- See J2SE 5.0 API Specification, java.lang.management.MemoryManagerMXBean +-- for more information on memory managers, and +-- java.lang.management.GarbageCollectorMXBean for more information on +-- garbage collectors. +-- +----------------------------------------------------------------------- + +jvmMemGCTable OBJECT-TYPE + SYNTAX SEQUENCE OF JvmMemGCEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Garbage Collector table provides additional information + on those MemoryManagers which are also GarbageCollectors. + This table extends the jvmMemManagerTable table. The index + used in the jvmMemGCTable table is imported from the + jvmMemManagerTable table. If a row from the jvmMemManagerTable + table is deleted, and if it has an extension in the jvmMemGCTable + table, then the extension row will also be deleted. + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.GarbageCollectorMXBean" + ::= { jvmMemory 101 } + +jvmMemGCEntry OBJECT-TYPE + SYNTAX JvmMemGCEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Provide additional information on Garbage Collectors. + + Columnar objects in this table are mapped from the + GarbageCollectorMXBean interface. + + See java.lang.management.GarbageCollectorMXBean + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.GarbageCollectorMXBean" + INDEX { jvmMemManagerIndex } + ::= {jvmMemGCTable 1 } + +JvmMemGCEntry ::= SEQUENCE { + jvmMemGCCount Counter64, + jvmMemGCTimeMs JvmTimeMillis64TC +} + +jvmMemGCCount OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of collections that have occurred, + as returned by GarbageCollectorMXBean.getCollectionCount(). + + If garbage collection statistics are not available, this + object is set to 0. + + See java.lang.management.GarbageCollectorMXBean.getCollectionCount() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.GarbageCollectorMXBean" + ::= { jvmMemGCEntry 2 } + +jvmMemGCTimeMs OBJECT-TYPE + SYNTAX JvmTimeMillis64TC + UNITS "milliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The approximate accumulated collection elapsed time in + milliseconds, since the Java virtual machine has started. + This object is set to 0 if the collection elapsed time is + undefined for this collector. + + See java.lang.management.GarbageCollectorMXBean.getCollectionTime() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.GarbageCollectorMXBean" + DEFVAL { 0 } + ::= { jvmMemGCEntry 3 } + +-- The JVM Memory Pool Table +-- +-- The jvmMemPoolTable represent memory pool abstract entities. +-- The jvmMemPoolTable contains one row per memory pool. +-- +-- See J2SE 5.0 API Specification, java.lang.management.MemoryMXBean for +-- a detailed description of the memory subsystem. +-- +-- See J2SE 5.0 API Specification, java.lang.management.MemoryPoolMXBean +-- for more information on memory pool. +-- +----------------------------------------------------------------------- +-- +-- We use the range jvmMemory.[110..119] for objects related to memory pools. +-- Object identifiers in the range jvmMemory.[111-119] are not used but +-- are reserved for future evolution of this MIB. +-- +jvmMemPoolTable OBJECT-TYPE + SYNTAX SEQUENCE OF JvmMemPoolEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Memory Pool Table contains the whole list of MemoryPools + as returned by ManagementFactory.getMemoryPoolMXBeans(). + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean" + ::= { jvmMemory 110 } + +jvmMemPoolEntry OBJECT-TYPE + SYNTAX JvmMemPoolEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + " + Represents a memory pool. The pool may contain heap memory or + non-heap memory. A row in this table represents + an instance of MemoryPoolMXBean. + + See java.lang.management.MemoryPoolMXBean + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean" + INDEX { jvmMemPoolIndex } + ::= { jvmMemPoolTable 1 } + +JvmMemPoolEntry ::= SEQUENCE { + jvmMemPoolIndex JvmPositive32TC, + jvmMemPoolName JvmJavaObjectNameTC, + jvmMemPoolType JvmManagedMemoryTypeTC, + jvmMemPoolState JvmValidityStateTC, + jvmMemPoolPeakReset JvmTimeMillis64TC, + + jvmMemPoolInitSize JvmUnsigned64TC, + jvmMemPoolUsed JvmUnsigned64TC, + jvmMemPoolCommitted JvmUnsigned64TC, + jvmMemPoolMaxSize JvmUnsigned64TC, + + jvmMemPoolPeakUsed JvmUnsigned64TC, + jvmMemPoolPeakCommitted JvmUnsigned64TC, + jvmMemPoolPeakMaxSize JvmUnsigned64TC, + + jvmMemPoolCollectUsed JvmUnsigned64TC, + jvmMemPoolCollectCommitted JvmUnsigned64TC, + jvmMemPoolCollectMaxSize JvmUnsigned64TC, + + jvmMemPoolThreshold JvmUnsigned64TC, + jvmMemPoolThreshdCount Counter64, + jvmMemPoolThreshdSupport JvmImplSupportStateTC, + jvmMemPoolCollectThreshold JvmUnsigned64TC, + jvmMemPoolCollectThreshdCount Counter64, + jvmMemPoolCollectThreshdSupport JvmImplSupportStateTC + +} + +jvmMemPoolIndex OBJECT-TYPE + SYNTAX JvmPositive32TC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An index value opaquely computed by the agent which uniquely + identifies a row in the jvmMemPoolTable. + + The jvmMemPoolIndex index is opaquely computed by the agent, + from e.g the hash code of the MemoryPool (or MemoryPool name). + The agent is responsible for allocating a free index when it + needs one (e.g. if two objects have the same hash, then it may + increment one of the values until the conflict is resolved). + As a result a manager must not depend on the value of that + index across, e.g. reboot of the agent, as this value is not + guaranteed to stay identical after the agent restarts. + " + ::= { jvmMemPoolEntry 1 } + +jvmMemPoolName OBJECT-TYPE + SYNTAX JvmJavaObjectNameTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The name of this memory pool, as returned by + MemoryPoolMXBean.getName(). + + See java.lang.management.MemoryPoolMXBean.getName() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean" + ::= { jvmMemPoolEntry 2 } + +jvmMemPoolType OBJECT-TYPE + SYNTAX JvmManagedMemoryTypeTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The type of memory managed in this pool. This pool may be used for + heap memory or non-heap memory. + + See java.lang.management.MemoryPoolMXBean.getMemoryType() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean" + ::= { jvmMemPoolEntry 3 } + +jvmMemPoolState OBJECT-TYPE + SYNTAX JvmValidityStateTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + Indicates whether this memory pool is valid in the Java + virtual machine. A memory pool becomes invalid once the + Java virtual machine removes it from the memory system. + + See java.lang.management.MemoryPoolMXBean.isValid() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean" + ::= { jvmMemPoolEntry 4 } + +jvmMemPoolPeakReset OBJECT-TYPE + SYNTAX JvmTimeMillis64TC + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + " + This object indicates the last time - in milliseconds - at which + the peak memory usage statistic of this memory pool was reset + to the current memory usage. This corresponds to a time stamp + as returned by java.lang.System.currentTimeMillis(); + + Setting this object to a time earlier than its current time value + has no effect. Setting this object to a time later than its current + time value causes the peak memory usage statistic of this memory + pool to be reset to the current memory usage. The new value of this + object will be the time at which the reset operation is triggered. + + There could be a delay between the time at which the reset operation + is triggered and the time at which the actual resetting happens, so + this value is only indicative. + + See java.lang.management.MemoryPoolMXBean.resetPeakUsage() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean" + ::= { jvmMemPoolEntry 5 } + + +-- The object identifier arcs in the range jvmMemPoolEntry.[6-9] are +-- reserved for future evolution of this MIB. +-- +-- We use the range jvmMemPoolEntry.[10..19] for objects related to this +-- pool memory usage, as returned by +-- java.lang.management.MemoryPoolMXBean.getUsage(). +-- Object identifiers in the range jvmMemPoolEntry.[14..19] are not +-- used but are reserved for future evolution of this MIB. +-- +jvmMemPoolInitSize OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + Initial size of this memory pool. + + See java.lang.management.MemoryPoolMXBean.getUsage().getInit() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean, + java.lang.management.MemoryUsage" + ::= { jvmMemPoolEntry 10 } + + +jvmMemPoolUsed OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + Amount of used memory in this memory pool. + + See java.lang.management.MemoryPoolMXBean.getUsage().getUsed() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean, + java.lang.management.MemoryUsage" + ::= { jvmMemPoolEntry 11 } + +jvmMemPoolCommitted OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + Amount of committed memory in this memory pool. + + See java.lang.management.MemoryPoolMXBean.getUsage().getCommitted() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean, + java.lang.management.MemoryUsage" + ::= { jvmMemPoolEntry 12 } + +jvmMemPoolMaxSize OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + Maximal size of this memory pool. + + See java.lang.management.MemoryPoolMXBean.getUsage().getMax() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean, + java.lang.management.MemoryUsage" + ::= { jvmMemPoolEntry 13 } + +-- We use the range jvmMemPoolEntry.[20..29] for objects related to +-- this pool peak memory usage, as returned by +-- java.lang.management.MemoryPoolMXBean.getPeakUsage(). +-- The object identifier arc jvmMemPoolEntry.20 which would have been +-- used for the initial size is not used because the notion of initial +-- size in the context of peak usage is meaningless. +-- Therefore, we start numbering objects at 21. +-- Object identifiers in the range jvmMemPoolEntry.[24..29] are not +-- used but are reserved for future evolution of this MIB. +-- +jvmMemPoolPeakUsed OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + Amount of used memory in this memory pool when the peak usage + was reached. + + See java.lang.management.MemoryPoolMXBean.getPeakUsage().getUsed() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean, + java.lang.management.MemoryUsage" + ::= { jvmMemPoolEntry 21 } + +jvmMemPoolPeakCommitted OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + Amount of committed memory in this memory pool when the peak usage + was reached. + + See java.lang.management.MemoryPoolMXBean.getPeakUsage().getCommitted() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean, + java.lang.management.MemoryUsage" + ::= { jvmMemPoolEntry 22 } + +jvmMemPoolPeakMaxSize OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + Maximal size of this memory pool when the peak usage + was reached. + + See java.lang.management.MemoryPoolMXBean.getPeakUsage().getMax() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean, + java.lang.management.MemoryUsage" + ::= { jvmMemPoolEntry 23 } + +-- We use the range jvmMemPoolEntry.[30..39] for objects related to this +-- pool collection memory usage, as returned by +-- java.lang.management.MemoryPoolMXBean.getCollectionUsage(). +-- The object identifier arc jvmMemPoolEntry.30 which would have been used +-- for the initial size is not used because the notion of initial size in the +-- context of collection usage is meaningless. +-- Therefore, we start numbering objects at 31. +-- Object identifiers in the range jvmMemPoolEntry.[34..39] are not used +-- but are reserved for future evolution of this MIB. +-- +jvmMemPoolCollectUsed OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + The amount of used memory at the most recent time that the + Java virtual machine has expended effort in recycling unused objects + in this memory pool. + + See java.lang.management.MemoryPoolMXBean.getCollectionUsage().getUsed() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean, + java.lang.management.MemoryUsage" + ::= { jvmMemPoolEntry 31 } + +jvmMemPoolCollectCommitted OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + The amount of committed memory at the most recent time that the + Java virtual machine has expended effort in recycling unused objects + in this memory pool. + + See java.lang.management.MemoryPoolMXBean.getCollectionUsage(). + getCommitted() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean, + java.lang.management.MemoryUsage" + ::= { jvmMemPoolEntry 32 } + +jvmMemPoolCollectMaxSize OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + " + The value of the maximum amount of memory at the most recent time + that the Java virtual machine has expended effort in recycling + unused objects in this memory pool. + + See java.lang.management.MemoryPoolMXBean.getCollectionUsage().getMax() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean, + java.lang.management.MemoryUsage" + ::= { jvmMemPoolEntry 33 } + +-- Object identifiers in the range jvmMemPoolEntry.[40-109] are reserved +-- for future evolution of this MIB. +-- +-- We use the range jvmMemPoolEntry.[110..119] for objects related to this +-- pool memory usage thresholds (range jvmMemPoolEntry.[10..19] was used for +-- this pool memory usage). +-- Object identifier arcs in the range jvmMemPoolEntry.[113..119] are not +-- used but are reserved for future evolution of this MIB. +-- +jvmMemPoolThreshold OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The threshold value for the memory usage of this memory pool, + in bytes. A zero value (0) indicates that no threshold value is + configured. + When the amount of used memory crosses over this threshold + value the JVM will trigger a usage memory threshold exceeded + notification, and the jvmMemPoolThreshdCount increases. + + If memory usage threshold is not supported, then this object, if + implemented, will always be equals to 0. In that case, attempting + to set this object will trigger an inconsistentValue error. + + See also jvmMemPoolThreshdSupport. + + See java.lang.management.MemoryPoolMXBean.getUsageThreshold(), + java.lang.management.MemoryPoolMXBean.setUsageThreshold(long), + java.lang.management.MemoryPoolMXBean.getUsageThresholdCount(), + java.lang.management.MemoryPoolMXBean.isUsageThresholdSupported() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean" + DEFVAL { 0 } + ::= { jvmMemPoolEntry 110 } + +jvmMemPoolThreshdCount OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times that the memory usage has crossed + the usage threshold, as detected by the Java virtual machine. + + If memory usage threshold is not supported, then this object, if + implemented, will always be equals to 0. + + See also jvmMemPoolThresholdSupport. + + See java.lang.management.MemoryPoolMXBean.getUsageThresholdCount(), + java.lang.management.MemoryPoolMXBean.isUsageThresholdSupported() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean" + ::= { jvmMemPoolEntry 111 } + +jvmMemPoolThreshdSupport OBJECT-TYPE + SYNTAX JvmImplSupportStateTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Tells whether this memory pool supports usage threshold. + + See java.lang.management.MemoryPoolMXBean.isUsageThresholdSupported() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean" + ::= { jvmMemPoolEntry 112 } + +-- Object identifiers in the range jvmMemPoolEntry.[120-129] are reserved +-- for future evolution of this MIB. +-- +-- We use the range jvmMemPoolEntry.[130..139] for objects related to +-- this pool memory collection usage thresholds (range +-- jvmMemPoolEntry.[30..39] was used for this pool collection memory usage). +-- Object identifiers in the range jvmMemPoolEntry.[133..139] are not used +-- but are reserved for future evolution of this MIB. +-- +jvmMemPoolCollectThreshold OBJECT-TYPE + SYNTAX JvmUnsigned64TC + UNITS "bytes" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The threshold value for the collection usage of this memory pool, + in bytes. A zero value (0) indicates that no threshold value is + configured. + When the amount of used memory crosses over this threshold + value the JVM will trigger a collection memory threshold exceeded + notification, and the jvmMemPoolCollectThreshdCount increases. + + If collection usage threshold is not supported, then this object, if + implemented, will always be equals to 0. In that case, attempting + to set this object will trigger an inconsistentValue error. + + See also jvmMemPoolCollectThreshdSupport. + + See java.lang.management.MemoryPoolMXBean. + getCollectionUsageThreshold(), + java.lang.management.MemoryPoolMXBean. + setCollectionUsageThreshold(long), + java.lang.management.MemoryPoolMXBean. + isCollectionUsageThresholdSupported(), + java.lang.management.MemoryPoolMXBean. + getCollectionUsageThresholdCount() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean" + DEFVAL { 0 } + ::= { jvmMemPoolEntry 131 } + +jvmMemPoolCollectThreshdCount OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times that the memory usage has crossed + the collection usage threshold, as detected by the Java virtual + machine. + + If memory usage threshold is not supported, then this object, if + implemented, will always be equals to 0. + + See also jvmMemPoolCollectThreshdSupport. + + See java.lang.management.MemoryPoolMXBean. + getCollectionUsageThresholdCount(), + java.lang.management.MemoryPoolMXBean. + isCollectionUsageThresholdSupported() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean" + ::= { jvmMemPoolEntry 132 } + +jvmMemPoolCollectThreshdSupport OBJECT-TYPE + SYNTAX JvmImplSupportStateTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Tells whether this memory pool supports collection usage threshold. + + See java.lang.management.MemoryPoolMXBean. + isCollectionUsageThresholdSupported() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean" + ::= { jvmMemPoolEntry 133 } + +-- The JVM Memory Manager-Pool Relation Table +----------------------------------------------------------------------- +-- The JVM Memory Pool Table +-- +-- The jvmMemPoolTable represent memory pool abstract entities. +-- The jvmMemPoolTable contains one row per memory pool. +-- +-- See J2SE 5.0 API Specification, java.lang.management.MemoryMXBean for +-- a detailed description of the memory subsystem. +-- +-- See J2SE 5.0 API Specification, java.lang.management.MemoryPoolMXBean +-- for more information on memory pool. +-- +----------------------------------------------------------------------- +-- +-- We use the range jvmMemory.[110..119] for objects related to memory pools. +-- Object identifier arcs in the range jvmMemory.[111-119] are not used +-- but are reserved for future evolution of this MIB. +-- + +jvmMemMgrPoolRelTable OBJECT-TYPE + SYNTAX SEQUENCE OF JvmMemMgrPoolRelEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Memory Manager-Pool Relation Table shows the + Memory Manager / Memory Pool relations, as returned by + MemoryPoolMXBean.getMemoryManagerNames() and + MemoryManagerMXBean.getMemoryPoolNames(). + This table imports the indexes from the jvmMemManagerTable table + and jvmMemPoolTable table. The jvmMemMgrRelManagerName and + jvmMemMgrRelPoolName objects are not actually necessary since + the indexes are self-sufficient to express the relationship - + but the names will make the table more understandable when displayed + in a management console. + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean, + java.lang.management.MemoryManagerMXBean" + ::= { jvmMemory 120 } + +jvmMemMgrPoolRelEntry OBJECT-TYPE + SYNTAX JvmMemMgrPoolRelEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A row in this table indicates that the Memory Manager identified + by jvmMemManagerIndex manages the Memory Pool identified by + jvmMemPoolIndex. Note that a pool may be managed by several + memory managers, and a memory manager can manage several + memory pool. + + See java.lang.management.MemoryManagerMXBean.getMemoryPoolNames(), + java.lang.management.MemoryPoolMXBean.getMemoryManagerNames() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean, + java.lang.management.MemoryManagerMXBean" + INDEX { jvmMemManagerIndex, jvmMemPoolIndex } + ::= { jvmMemMgrPoolRelTable 1 } + +JvmMemMgrPoolRelEntry ::= SEQUENCE { + jvmMemMgrRelManagerName JvmJavaObjectNameTC, + jvmMemMgrRelPoolName JvmJavaObjectNameTC +} + +jvmMemMgrRelManagerName OBJECT-TYPE + SYNTAX JvmJavaObjectNameTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The name of the memory manager. + + See java.manangement.MemoryManagerMXBean.getName(); + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryManagerMXBean" + ::= { jvmMemMgrPoolRelEntry 2 } + +jvmMemMgrRelPoolName OBJECT-TYPE + SYNTAX JvmJavaObjectNameTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The name of the memory pool. + + See java.manangement.MemoryPoolMXBean.getName(); + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryPoolMXBean" + ::= { jvmMemMgrPoolRelEntry 3 } + + +----------------------------------------------------------------------- +-- +-- The JVM Thread group +-- +-- A collection of objects used to monitor threads in the +-- Java Virtual Machine. These objects define the SNMP management +-- interface for the thread system of the Java virtual machine. +-- +-- The jvmThreadInstanceTable represents the threads which are currently +-- alive in the system. The representation of a thread is derived from the +-- set of methods in the ThreadMXBean that return information about a +-- given thread. +-- +-- See J2SE 5.0 API Specification, java.lang.management.ThreadMXBean for +-- a detailed description of the threading subsystem. +-- +----------------------------------------------------------------------- + +-- +----------------------------------------------------------------------- + +jvmThreading OBJECT IDENTIFIER ::= { jvmMgtMIBObjects 3 } + +-- The following objects are mapped from the ThreadMXBean interface. +----------------------------------------------------------------------- + +jvmThreadCount OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The current number of live threads. + + See java.lang.management.ThreadMXBean.getThreadCount() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean" + ::= { jvmThreading 1 } + +jvmThreadDaemonCount OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The current number of daemon threads. + + See java.lang.management.ThreadMXBean.getDaemonThreadCount() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean" + ::= { jvmThreading 2 } + +jvmThreadPeakCount OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The peak thread count since the execution of the application. + + See java.lang.management.ThreadMXBean.getPeakThreadCount() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean" + ::= { jvmThreading 3 } + +jvmThreadTotalStartedCount OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of threads created and started since the Java + Virtual Machine started. + + See java.lang.management.ThreadMXBean.getTotalStartedThreadCount() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean" + ::= { jvmThreading 4 } + +jvmThreadContentionMonitoring OBJECT-TYPE + SYNTAX JvmImplOptFeatureStateTC + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The state of the Thread Contention Monitoring feature. + This feature can be: + + unsupported: The JVM does not support Thread Contention Monitoring. + enabled : The JVM supports Thread Contention Monitoring, and it + is enabled. + disabled : The JVM supports Thread Contention Monitoring, and it + is disabled. + + Only enabled(3) and disabled(4) may be supplied as values to a + SET request. unsupported(1) can only be set internally by the + agent. + + When the feature is unsupported(1), any attempt to change + that value will fail: trying to set this object to + enabled(3) or disabled(4) will result in an `inconsistentValue' + error. Trying to set it to any other value will result in an + `wrongValue' error. + + See java.lang.management.ThreadMXBean. + isThreadContentionMonitoringSupported(), + java.lang.management.ThreadMXBean. + isThreadContentionMonitoringEnabled(), + java.lang.management.ThreadMXBean. + setThreadContentionMonitoringEnabled() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean" + ::= { jvmThreading 5 } + +jvmThreadCpuTimeMonitoring OBJECT-TYPE + SYNTAX JvmImplOptFeatureStateTC + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The state of the Thread CPU Time Monitoring feature. + This feature can be: + + unsupported: The JVM does not support Thread CPU Time Monitoring. + enabled : The JVM supports Thread CPU Time Monitoring, and it + is enabled. + disabled : The JVM supports Thread CPU Time Monitoring, and it + is disabled. + + Only enabled(3) and disabled(4) may be supplied as values to a + SET request. unsupported(1) can only be set internally by the + agent. + + When the feature is unsupported(1), any attempt to change + that value will fail: trying to set this object to + enabled(3) or disabled(4) will result in an `inconsistentValue' + error. Trying to set it to any other value will result in an + `wrongValue' error. + + See java.lang.management.ThreadMXBean. + isThreadCpuTimeSupported(), + java.lang.management.ThreadMXBean. + isThreadCpuTimeEnabled(), + java.lang.management.ThreadMXBean. + setThreadCpuTimeEnabled() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean" + ::= { jvmThreading 6 } + +jvmThreadPeakCountReset OBJECT-TYPE + SYNTAX JvmTimeMillis64TC + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + " + This object indicates the last time - in milliseconds - at which + the peak thread count was reset to the current thread count. + This corresponds to a time stamp as returned by + java.lang.System.currentTimeMillis(). + + Setting this object to a time earlier than its current time value + has no effect. Setting this object to a time later than its current + time value causes the peak thread count statistic to be reset to + the current thread count. The new value of this object will be + the time at which the reset operation is triggered. + + There could be a delay between the time at which the reset operation + is triggered and the time at which the actual resetting happens, so + this value is only indicative. + + See java.lang.management.ThreadMXBean.resetPeakThreadCount() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean" + ::= { jvmThreading 7 } + + +-- Object identifiers in the range jvmThreading.[8-10] are reserved +-- for future evolution of this MIB. +-- +----------------------------------------------------------------------- +-- The JVM Thread Instance Table +-- +-- The jvmThreadInstanceTable represents the threads which are currently +-- alive in the system. The representation of a thread is derived from the +-- set of methods in the ThreadMXBean that return information about a +-- given thread. +-- +-- See J2SE 5.0 API Specification, java.lang.management.ThreadMXBean for +-- a detailed description of the threading subsystem. +-- See also J2SE 5.0 API Specification, java.lang.management.ThreadInfo, +-- and java.lang.Thread +-- +----------------------------------------------------------------------- + +jvmThreadInstanceTable OBJECT-TYPE + SYNTAX SEQUENCE OF JvmThreadInstanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Thread Instance Table is built from all the methods of + ThreadMXBean that take a ThreadID as parameter. + + See java.lang.management.ThreadMXBean.getAllThreadIds() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean" + ::= { jvmThreading 10 } + +jvmThreadInstanceEntry OBJECT-TYPE + SYNTAX JvmThreadInstanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A row in this table represents a live thread. + Attributes in this row are built from all the methods of + ThreadMXBean that take a ThreadID as parameter. + + See java.lang.management.ThreadMXBean + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean" + INDEX { jvmThreadInstIndex } + ::= { jvmThreadInstanceTable 1 } + +JvmThreadInstanceEntry ::= SEQUENCE { + jvmThreadInstIndex JvmIndex64TC, + jvmThreadInstId JvmUnsigned64TC, + jvmThreadInstState JvmThreadStateTC, + jvmThreadInstBlockCount Counter64, + jvmThreadInstBlockTimeMs JvmTimeMillis64TC, + jvmThreadInstWaitCount Counter64, + jvmThreadInstWaitTimeMs JvmTimeMillis64TC, + jvmThreadInstCpuTimeNs JvmTimeNanos64TC, + jvmThreadInstLockName JvmJavaObjectNameTC, + jvmThreadInstLockOwnerPtr RowPointer, + jvmThreadInstName JvmJavaObjectNameTC +} + +jvmThreadInstIndex OBJECT-TYPE + SYNTAX JvmIndex64TC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An index uniquely identifying a live thread, and directly + derived from the value of jvmThreadInstId. The jvmThreadInstId + cannot be used directly as index in the table, because integer + indexes cannot exceed an unsigned 32 int. + + The jvmThreadInstIndex index is an 8 byte octet string as + defined by the JvmIndex64TC TEXTUAL-CONVENTION. Its value is + directly derived from the value of the corresponding ThreadID + returned by jvmThreadInstId. + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean, java.lang.Thread" + ::= { jvmThreadInstanceEntry 1 } + +jvmThreadInstId OBJECT-TYPE + SYNTAX JvmUnsigned64TC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The thread ID, as returned by Thread.getId(). + + See java.lang.management.ThreadMXBean.getThreadInfo(long,boolean). + getThreadId() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean, java.lang.Thread" + ::= { jvmThreadInstanceEntry 2 } + +jvmThreadInstState OBJECT-TYPE + SYNTAX JvmThreadStateTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The state of this thread instance. + + See java.lang.management.ThreadMXBean.getThreadInfo(long,boolean). + getThreadState() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean" + ::= { jvmThreadInstanceEntry 3 } + +jvmThreadInstBlockCount OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of times that this thread has blocked to enter + or re-enter a monitor.. + + See java.lang.management.ThreadMXBean.getThreadInfo(long,boolean). + getBlockedCount() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean" + ::= { jvmThreadInstanceEntry 4 } + +jvmThreadInstBlockTimeMs OBJECT-TYPE + SYNTAX JvmTimeMillis64TC + UNITS "milliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The approximate accumulated elapsed time (in millisecond) + that a thread has blocked to enter or re-enter a monitor since + it has started - or since thread contention monitoring was + enabled. + + This object is always set to 0 if thread contention monitoring + is disabled or not supported. + + See java.lang.management.ThreadMXBean.getThreadInfo(long,boolean). + getBlockedTime() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean" + ::= { jvmThreadInstanceEntry 5 } + +jvmThreadInstWaitCount OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of times that this thread has waited for + notification. + + See java.lang.management.ThreadMXBean.getThreadInfo(long,boolean). + getWaitedCount() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean" + ::= { jvmThreadInstanceEntry 6 } + +jvmThreadInstWaitTimeMs OBJECT-TYPE + SYNTAX JvmTimeMillis64TC + UNITS "milliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The approximate accumulated elapsed time (in millisecond) + that a thread has waited on a monitor through a + java.lang.Object.wait method since it has started - or since + thread contention monitoring wasenabled. + + This object is always set to 0 if thread contention monitoring + is disabled or not supported. + + See java.lang.management.ThreadMXBean.getThreadInfo(long,boolean). + getWaitedTime() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean" + ::= { jvmThreadInstanceEntry 7 } + +jvmThreadInstCpuTimeNs OBJECT-TYPE + SYNTAX JvmTimeNanos64TC + UNITS "nanoseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The approximate accumulated CPU time (in nanosecond) for a thread + since it has started - or since thread CPU time monitoring was + enabled. + + If the thread of the specified ID is not alive or does not exist, + or the CPU time measurement is disabled or not supported, + this object is set to 0. + + See java.lang.management.ThreadMXBean.getThreadCpuTime(long), + java.lang.management.ThreadMXBean.isThreadCpuTimeSupported(), + java.lang.management.ThreadMXBean.isThreadCpuTimeEnabled() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean" + ::= { jvmThreadInstanceEntry 8 } + +jvmThreadInstName OBJECT-TYPE + SYNTAX JvmJavaObjectNameTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This thread name - as returned by Thread.getThreadName(). + + See java.lang.management.ThreadInfo.getThreadName() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean, + java.lang.management.ThreadInfo" + ::= { jvmThreadInstanceEntry 9 } + +jvmThreadInstLockName OBJECT-TYPE + SYNTAX JvmJavaObjectNameTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The string representation of the monitor lock that this thread + is blocked to enter or waiting to be notified through the + Object.wait method. + + See J2SE 5.0 API Specification, + java.lang.management.ThreadInfo.getLockName() + for more information on the format of this string. + + If this thread is not blocked then a zero-length string is returned. + + Note that the SNMP agent may have to truncate the string returned + by the underlying API if it does not fit in the JvmJavaObjectNameTC + (1023 bytes max). + + See java.lang.management.ThreadInfo.getLockName() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean, + java.lang.management.ThreadInfo" + ::= { jvmThreadInstanceEntry 10 } + +jvmThreadInstLockOwnerPtr OBJECT-TYPE + SYNTAX RowPointer + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "A pointer to the thread which owns the monitor of the + object on which this thread instance is blocked. + This object will point to jvmThreadInstId of the + lock owner thread. + + If this thread is not blocked then 0.0 is returned. + + See java.lang.management.ThreadInfo.getLockOwnerId() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.ThreadMXBean, + java.lang.management.ThreadInfo" + ::= { jvmThreadInstanceEntry 11 } + +----------------------------------------------------------------------- +-- +-- The JVM Runtime group +-- +-- A collection of objects used to monitor the Java Virtual Machine +-- Runtime. These objects define the SNMP management interface for the +-- runtime system of the Java virtual machine. +-- +-- The JVM Runtime group defines object mapped from the +-- java.lang.management.RuntimeMXBean interface. +-- +-- See J2SE 5.0 API Specification, java.lang.management.RuntimeMXBean for +-- a detailed description of the runtime system. +-- +----------------------------------------------------------------------- + +jvmRuntime OBJECT IDENTIFIER ::= { jvmMgtMIBObjects 4 } + +-- The following objects are mapped from the RuntimeMXBean interface. +----------------------------------------------------------------------- + +jvmRTName OBJECT-TYPE + SYNTAX DisplayString + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The name representing the running Java virtual machine. + + Note that the SNMP agent may have to truncate the name returned + by the underlying API if it does not fit in the DisplayString + (255 bytes max). + + See java.lang.management.RuntimeMXBean.getName() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRuntime 1 } + +jvmRTVMName OBJECT-TYPE + SYNTAX JvmJavaObjectNameTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Java virtual machine implementation name. + + See java.lang.management.RuntimeMXBean.getVmName() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRuntime 2 } + +jvmRTVMVendor OBJECT-TYPE + SYNTAX DisplayString + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Java virtual machine implementation vendor. + + Note that the SNMP agent may have to truncate the string returned + by the underlying API if it does not fit in the DisplayString + (255 bytes max). + + See java.lang.management.RuntimeMXBean.getVmVendor() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRuntime 3 } + +jvmRTVMVersion OBJECT-TYPE + SYNTAX DisplayString + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Java virtual machine implementation version. + + Note that the SNMP agent may have to truncate the string returned + by the underlying API if it does not fit in the DisplayString + (255 bytes max). + + See java.lang.management.RuntimeMXBean.getVmVersion() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRuntime 4 } + +jvmRTSpecName OBJECT-TYPE + SYNTAX DisplayString + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Java virtual machine specification name. + + Note that the SNMP agent may have to truncate the string returned + by the underlying API if it does not fit in the DisplayString + (255 bytes max). + + See java.lang.management.RuntimeMXBean.getSpecName() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRuntime 5 } + +jvmRTSpecVendor OBJECT-TYPE + SYNTAX DisplayString + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Java virtual machine specification vendor. + + Note that the SNMP agent may have to truncate the string returned + by the underlying API if it does not fit in the DisplayString + (255 bytes max). + + See java.lang.management.RuntimeMXBean.getSpecVendor() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRuntime 6 } + +jvmRTSpecVersion OBJECT-TYPE + SYNTAX DisplayString + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Java virtual machine specification version. + + Note that the SNMP agent may have to truncate the string returned + by the underlying API if it does not fit in the DisplayString + (255 bytes max). + + See java.lang.management.RuntimeMXBean.getSpecVersion() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRuntime 7 } + +jvmRTManagementSpecVersion OBJECT-TYPE + SYNTAX DisplayString + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The version of the management specification for the Java virtual + machine implementation. + + Note that the SNMP agent may have to truncate the string returned + by the underlying API if it does not fit in the DisplayString + (255 bytes max). + + See java.lang.management.RuntimeMXBean.getManagementSpecVersion() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRuntime 8 } + +jvmRTBootClassPathSupport OBJECT-TYPE + SYNTAX JvmImplSupportStateTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Indicates whether the Java virtual machine supports the + boot class path mechanism used by the bootstrap class loader + to search for class files. + + See java.lang.management.RuntimeMXBean.isBootClassPathSupported() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRuntime 9 } + +jvmRTInputArgsCount OBJECT-TYPE + SYNTAX JvmPositive32TC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of input arguments passed to the Java Virtual Machine. + + See java.lang.management.RuntimeMXBean.getInputArguments() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRuntime 10 } + +jvmRTUptimeMs OBJECT-TYPE + SYNTAX JvmTimeMillis64TC + UNITS "milliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Uptime of the Java virtual machine, in milliseconds. This is + equivalent to ( System.currentTimeMillis() - jvmStartTimeMs ). + + See also jvmRTStartTimeMs. + + See java.lang.management.RuntimeMXBean.getUptime() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRuntime 11 } + +jvmRTStartTimeMs OBJECT-TYPE + SYNTAX JvmTimeMillis64TC + UNITS "milliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The approximate time when the Java virtual machine started, in + milliseconds. This is a time stamp as returned by + System.currentTimeMillis(). This time will not change unless + the Java Virtual Machine is restarted. + + See also jvmRTUptimeMs. + + See java.lang.management.RuntimeMXBean.getStartTime() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRuntime 12 } + + +-- Object identifiers in the range jvmRuntime.[13-19] are reserved +-- for future evolution of this MIB. +-- +----------------------------------------------------------------------- +-- +-- The JVM Input Argument Table +-- +-- The jvmRTInputArgsTable contains one row per input argument given on +-- the Java command line. +-- +-- See J2SE 5.0 API Specification, +-- java.lang.management.RuntimeMXBean.getInputArguments() +-- for more information. +----------------------------------------------------------------------- + +jvmRTInputArgsTable OBJECT-TYPE + SYNTAX SEQUENCE OF JvmRTInputArgsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Input Argument Table lists the input arguments passed + to the Java Virtual Machine. + + The jvmRTInputArgsIndex is the index of the argument in + the array returned by RuntimeMXBean.getInputArguments(). + + See java.lang.management.RuntimeMXBean.getInputArguments() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRuntime 20 } + +jvmRTInputArgsEntry OBJECT-TYPE + SYNTAX JvmRTInputArgsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Represent an input argument passed to the Java Virtual Machine. + + See java.lang.management.RuntimeMXBean.getInputArguments() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + INDEX { jvmRTInputArgsIndex } + ::= { jvmRTInputArgsTable 1 } + +JvmRTInputArgsEntry ::= SEQUENCE { + jvmRTInputArgsIndex JvmPositive32TC, + jvmRTInputArgsItem JvmArgValueTC +} + +jvmRTInputArgsIndex OBJECT-TYPE + SYNTAX JvmPositive32TC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The index of the input argument, as in the array returned + by RuntimeMXBean.getInputArguments(). + + See java.lang.management.RuntimeMXBean.getInputArguments() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRTInputArgsEntry 1 } + +jvmRTInputArgsItem OBJECT-TYPE + SYNTAX JvmArgValueTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An input argument at index jvmRTInputArgsIndex, as in the array + returned by RuntimeMXBean.getInputArguments(). + + Note that the SNMP agent may have to truncate the string returned + by the underlying API if it does not fit in the JvmArgValueTC + (1023 bytes max). + + See java.lang.management.RuntimeMXBean.getInputArguments() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRTInputArgsEntry 2 } + + +----------------------------------------------------------------------- +-- +-- The JVM Boot Class Path Table +-- +-- The jvmRTBootClassPathTable contains one row per path element in the +-- bootclasspath. This table may not be implemented (or may be empty) if +-- the bootclasspath feature is not supported by the underlying +-- implementation. +-- +-- See J2SE 5.0 API Specification, +-- java.lang.management.RuntimeMXBean.getBootClassPath() +-- java.lang.management.RuntimeMXBean.isBootClassPathSupported() +-- for more information. +----------------------------------------------------------------------- + +jvmRTBootClassPathTable OBJECT-TYPE + SYNTAX SEQUENCE OF JvmRTBootClassPathEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The boot class path that is used by the bootstrap class loader + to search for a class file for loading. + + Note that the SNMP agent may have to truncate the bootclasspath + elements contained in the string returned by the underlying API + if it does not fit in the JvmPathElementTC (1023 bytes max). + + This table is not implemented (or empty) if jvmRTBootClassPathSupport + is unsupported(1). + + See java.lang.management.RuntimeMXBean.getBootClassPath() + java.lang.management.RuntimeMXBean.isBootClassPathSupported() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRuntime 21 } + +jvmRTBootClassPathEntry OBJECT-TYPE + SYNTAX JvmRTBootClassPathEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Represent a path element in the Java Virtual Machine bootclasspath. + + See java.lang.management.RuntimeMXBean.getBootClassPath() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + INDEX { jvmRTBootClassPathIndex } + ::= { jvmRTBootClassPathTable 1 } + +JvmRTBootClassPathEntry ::= SEQUENCE { + jvmRTBootClassPathIndex JvmPositive32TC, + jvmRTBootClassPathItem JvmPathElementTC +} + +jvmRTBootClassPathIndex OBJECT-TYPE + SYNTAX JvmPositive32TC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The index of the path element, as in the array obtained + by splitting RuntimeMXBean.getBootClassPath() in its elementary path + constituents. + + See java.lang.management.RuntimeMXBean.getBootClassPath() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRTBootClassPathEntry 1 } + +jvmRTBootClassPathItem OBJECT-TYPE + SYNTAX JvmPathElementTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An path element at index jvmRTBootClassPathIndex, as in the + array obtained by splitting RuntimeMXBean.getBootClassPath() in + its elementary path constituents. + + Note that the SNMP agent may have to truncate the string returned + by the underlying API if it does not fit in the JvmPathElementTC + (1023 bytes max). + + See java.lang.management.RuntimeMXBean.getBootClassPath() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRTBootClassPathEntry 2 } + +----------------------------------------------------------------------- +-- +-- The JVM Class Path Table +-- +-- The jvmRTClassPathTable contains one row per path element in the +-- classpath. +-- +-- See J2SE 5.0 API Specification, +-- java.lang.management.RuntimeMXBean.getClassPath() +-- for more information. +----------------------------------------------------------------------- + +jvmRTClassPathTable OBJECT-TYPE + SYNTAX SEQUENCE OF JvmRTClassPathEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The class path that is used by the system class loader + to search for a class file. + + Note that the SNMP agent may have to truncate the classpath + elements contained in the string returned by the underlying API + if it does not fit in the JvmPathElementTC (1023 bytes max). + + See java.lang.management.RuntimeMXBean.getClassPath() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRuntime 22 } + +jvmRTClassPathEntry OBJECT-TYPE + SYNTAX JvmRTClassPathEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Represent a path element in the Java Virtual Machine classpath. + + See java.lang.management.RuntimeMXBean.getClassPath() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + INDEX { jvmRTClassPathIndex } + ::= { jvmRTClassPathTable 1 } + +JvmRTClassPathEntry ::= SEQUENCE { + jvmRTClassPathIndex JvmPositive32TC, + jvmRTClassPathItem JvmPathElementTC +} + +jvmRTClassPathIndex OBJECT-TYPE + SYNTAX JvmPositive32TC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The index of the path element, as in the array obtained + by splitting RuntimeMXBean.getClassPath() in its elementary + path constituents. + + See java.lang.management.RuntimeMXBean.getClassPath() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRTClassPathEntry 1 } + +jvmRTClassPathItem OBJECT-TYPE + SYNTAX JvmPathElementTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An path element at index jvmRTClassPathIndex, as in the array + obtained by splitting RuntimeMXBean.getClassPath() in its elementary + path constituents. + + Note that the SNMP agent may have to truncate the string returned + by the underlying API if it does not fit in the JvmPathElementTC + (1023 bytes max). + + See java.lang.management.RuntimeMXBean.getClassPath() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRTClassPathEntry 2 } + +----------------------------------------------------------------------- +-- +-- The JVM Library Path Table +-- +-- The jvmRTLibraryPathTable contains one row per path element in the +-- librarypath. +-- +-- See J2SE 5.0 API Specification, +-- java.lang.management.RuntimeMXBean.getLibraryPath() +-- for more information. +----------------------------------------------------------------------- + +jvmRTLibraryPathTable OBJECT-TYPE + SYNTAX SEQUENCE OF JvmRTLibraryPathEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The library path. + + Note that the SNMP agent may have to truncate the librarypath + elements contained in the string returned by the underlying API + if it does not fit in the JvmPathElementTC (1023 bytes max). + + See java.lang.management.RuntimeMXBean.getLibraryPath() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRuntime 23 } + +jvmRTLibraryPathEntry OBJECT-TYPE + SYNTAX JvmRTLibraryPathEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Represent a path element in the Java Virtual Machine librarypath. + + See java.lang.management.RuntimeMXBean.getLibraryPath() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + INDEX { jvmRTLibraryPathIndex } + ::= { jvmRTLibraryPathTable 1 } + +JvmRTLibraryPathEntry ::= SEQUENCE { + jvmRTLibraryPathIndex JvmPositive32TC, + jvmRTLibraryPathItem JvmPathElementTC +} + +jvmRTLibraryPathIndex OBJECT-TYPE + SYNTAX JvmPositive32TC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The index of the path element, as in the array obtained + by splitting RuntimeMXBean.getLibraryPath() in its elementary + constituents. + + See java.lang.management.RuntimeMXBean.getLibraryPath() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRTLibraryPathEntry 1 } + +jvmRTLibraryPathItem OBJECT-TYPE + SYNTAX JvmPathElementTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An path element at index jvmRTLibraryPathIndex, as in the array + obtained by splitting RuntimeMXBean.getLibraryPath() in its elementary + path constituents. + + Note that the SNMP agent may have to truncate the string returned + by the underlying API if it does not fit in the JvmPathElementTC + (1023 bytes max). + + See java.lang.management.RuntimeMXBean.getLibraryPath() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.RuntimeMXBean" + ::= { jvmRTLibraryPathEntry 2 } + +----------------------------------------------------------------------- +-- +-- The JVM Compilation group +-- +-- A collection of objects used to monitor the Java Virtual Machine +-- Runtime Compiler (JIT). These objects define the SNMP management +-- interface for the compilation system of the Java virtual machine. +-- +-- The JVM Compilation group defines object mapped from the +-- java.lang.management.CompilationMXBean interface. +-- +-- See J2SE 5.0 API Specification, java.lang.management.CompilationMXBean for +-- a detailed description of the runtime system. +-- +----------------------------------------------------------------------- + +jvmCompilation OBJECT IDENTIFIER ::= { jvmMgtMIBObjects 5 } + +-- The following objects are mapped from the CompilationMXBean interface. +----------------------------------------------------------------------- + +jvmJITCompilerName OBJECT-TYPE + SYNTAX JvmJavaObjectNameTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The name of the Just-in-time (JIT) compiler. + + See java.lang.management.CompilationMXBean.getName() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.CompilationMXBean" + ::= { jvmCompilation 1 } + +jvmJITCompilerTimeMs OBJECT-TYPE + SYNTAX JvmTimeMillis64TC + UNITS "milliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Gets the approximate accumulated elapsed time (in milliseconds) + spent in compilation since the Java virtual machine has started. + If multiple threads are used for compilation, this value is + the summation of the approximate time that each thread + spent in compilation. + + If compiler time monitoring is not supported, then this object + remains set to 0. + + See java.lang.management.CompilationMXBean.getTotalCompilationTime() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.CompilationMXBean" + ::= { jvmCompilation 2 } + + +jvmJITCompilerTimeMonitoring OBJECT-TYPE + SYNTAX JvmImplSupportStateTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Indicates whether the Java virtual machine supports + compilation time monitoring. + + See java.lang.management.CompilationMXBean. + isCompilationTimeMonitoringSupported() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.CompilationMXBean" + ::= { jvmCompilation 3 } + +----------------------------------------------------------------------- +-- +-- The JVM Operating System group +-- +-- A collection of objects used to monitor some resource of the +-- Operating System the Java Virtual Machine is running on. These objects +-- define the SNMP management interface offered by the Java virtual machine +-- for the operating system on which it is running. +-- +-- The JVM Operating System group defines object mapped from the +-- java.lang.management.OperatingSystemMXBean interface. +-- +-- See J2SE 5.0 API Specification, java.lang.management.OperatingSystemMXBean +-- for a detailed description of the operating system. +-- +----------------------------------------------------------------------- + +jvmOS OBJECT IDENTIFIER ::= { jvmMgtMIBObjects 6 } + +-- The following objects are mapped from the OperatingSystemMXBean interface. +----------------------------------------------------------------------- + +jvmOSName OBJECT-TYPE + SYNTAX JvmJavaObjectNameTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The operating system name. + + See java.lang.management.OperatingSystemMXBean.getName() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.OperatingSystemMXBean" + ::= { jvmOS 1 } + +jvmOSArch OBJECT-TYPE + SYNTAX DisplayString + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The operating system architecture. + + Note that the SNMP agent may have to truncate the string returned + by the underlying API if it does not fit in the DisplayString + (255 bytes max). + + See java.lang.management.OperatingSystemMXBean.getArch() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.OperatingSystemMXBean" + ::= { jvmOS 2 } + +jvmOSVersion OBJECT-TYPE + SYNTAX DisplayString + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The operating system version. + + Note that the SNMP agent may have to truncate the string returned + by the underlying API if it does not fit in the DisplayString + (255 bytes max). + + See java.lang.management.OperatingSystemMXBean.getVersion() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.OperatingSystemMXBean" + ::= { jvmOS 3 } + +jvmOSProcessorCount OBJECT-TYPE + + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of processors available to the Java virtual machine. + + See java.lang.management.OperatingSystemMXBean.getAvailableProcessors() + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.OperatingSystemMXBean" + ::= { jvmOS 4 } + +-- +-- NOTIFICATIONS +-- +----------------------------------------------------------------------- + +-- +-- Low Memory Notifications +-- + +jvmMgtMIBMemoryNotifs OBJECT IDENTIFIER ::= { jvmMgtMIBNotifications 2 } +jvmMgtMIBLowMemoryNotifs OBJECT IDENTIFIER ::= { jvmMgtMIBMemoryNotifs 1 } + +jvmLowMemoryPrefix OBJECT IDENTIFIER + ::= { jvmMgtMIBLowMemoryNotifs 0 } + +-- Not used at this time, but reserved for future evolution of this MIB: +-- +-- jvmLowMemoryData OBJECT IDENTIFIER +-- ::= { jvmMgtMIBLowMemoryNotifs 1 } +-- + +jvmLowMemoryPoolUsageNotif NOTIFICATION-TYPE + OBJECTS { jvmMemPoolName, jvmMemPoolUsed, jvmMemPoolThreshdCount } + STATUS current + DESCRIPTION + "This notification is sent when the memory usage threshold of + a memory pool is exceeded. + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryNotification, + java.lang.management.MemoryPoolMXBean" + ::= { jvmLowMemoryPrefix 1 } + +jvmLowMemoryPoolCollectNotif NOTIFICATION-TYPE + OBJECTS { jvmMemPoolName, jvmMemPoolCollectUsed, + jvmMemPoolCollectThreshdCount } + STATUS current + DESCRIPTION + "This notification is sent when the collection memory usage + threshold of a memory pool is exceeded. + " + REFERENCE "J2SE 5.0 API Specification, + java.lang.management.MemoryNotification, + java.lang.management.MemoryPoolMXBean" + ::= { jvmLowMemoryPrefix 2 } + +-- +-- Conformance Section +-- +----------------------------------------------------------------------- + +-- conformance information + +jvmMgtMIBCompliances + OBJECT IDENTIFIER ::= { jvmMgtMIBConformance 1 } +jvmMgtMIBGroups + OBJECT IDENTIFIER ::= { jvmMgtMIBConformance 2 } + + +-- compliance statements + +jvmManagementCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for SNMP entities which + implement this MIB." + MODULE -- this module + MANDATORY-GROUPS { + jvmClassLoadingBasicGroup, + jvmClassLoadingSetGroup, + jvmMemoryBasicGroup, + jvmMemoryHeapUsageGroup, + jvmMemoryNonHeapUsageGroup, + jvmMemorySetGroup, + jvmMemManagerGroup, + jvmMemGCGroup, + jvmMemPoolBasicGroup, + jvmMemPoolUsageGroup, + jvmMemPoolPeakUsageGroup, + jvmMemPoolCollectUsageGroup, + jvmMemMgrPoolRelationGroup, + jvmThreadBasicGroup, + jvmThreadInstanceBasicGroup, + jvmRuntimeBasicGroup, + jvmOSGroup + } + + -- optional/conditional groups + GROUP jvmMemPoolMonitoringGroup + DESCRIPTION + "This group may not be implemented if the Java virtual + machine does not support low memory detection in memory usage. + " + GROUP jvmMemPoolCollectMonitoringGroup + DESCRIPTION + "This group may not be implemented if the Java virtual + machine does not support low memory detection in collection + memory usage. + " + GROUP jvmLowMemoryUsageNotifGroup + DESCRIPTION + "This group may not be implemented if the Java virtual + machine does not support low memory usage detection. + " + GROUP jvmLowMemoryCollectNotifGroup + DESCRIPTION + "This group may not be implemented if the Java virtual + machine does not support low collection memory usage detection. + " + GROUP jvmThreadInstanceCpuGroup + DESCRIPTION + "This group may not be implemented if the Java virtual + machine does not support CPU time measurement for other threads. + " + GROUP jvmThreadInstanceBlockGroup + DESCRIPTION + "This group may not be implemented if the Java virtual + machine does not support thread contention monitoring. + " + GROUP jvmRuntimeBootCPGroup + DESCRIPTION + "This group may not be implemented if the underlying + implementation does not support the bootclasspath feature. + " + GROUP jvmJITCompilerBasicGroup + DESCRIPTION + "This group may not be implemented if the Java virtual + machine has no compilation system. + " + GROUP jvmJITCompilerTimeStatGroup + DESCRIPTION + "This group may not be implemented if the Java virtual + machine has no compilation system, or does not support + JIT Compiler time statistics. + " + ::= { jvmMgtMIBCompliances 1 } + + +-- units of conformance + +jvmClassLoadingGroups OBJECT IDENTIFIER ::= { jvmMgtMIBGroups 1 } + +jvmClassLoadingBasicGroup OBJECT-GROUP + OBJECTS { + jvmClassesLoadedCount, + jvmClassesTotalLoadedCount, + jvmClassesUnloadedCount + } + STATUS current + DESCRIPTION + "A collection of objects that are mapped from JSR 163 + java.lang.management.ClassLoadingMXBean interface. + " + ::= { jvmClassLoadingGroups 1 } + +jvmClassLoadingSetGroup OBJECT-GROUP + OBJECTS { + jvmClassesVerboseLevel + } + STATUS current + DESCRIPTION + "A collection of writable scalar objects that are mapped from JSR 163 + java.lang.management.ClassLoadingMXBean interface, and make it possible + to act on class loading. Accessing these objects may + require special permissions - the agent implementation is + responsible for puting in place the appropriate access control + if needed. + " + ::= { jvmClassLoadingGroups 2 } + +jvmMemoryGroups OBJECT IDENTIFIER ::= { jvmMgtMIBGroups 2 } + +jvmMemoryBasicGroup OBJECT-GROUP + OBJECTS { + jvmMemoryPendingFinalCount + } + STATUS current + DESCRIPTION + "A collection of columnar objects that are mapped from JSR 163 + java.lang.management.MemoryManagerMXBean interface. + " + ::= { jvmMemoryGroups 1 } + +jvmMemoryHeapUsageGroup OBJECT-GROUP + OBJECTS { + jvmMemoryHeapInitSize, + jvmMemoryHeapUsed, + jvmMemoryHeapCommitted, + jvmMemoryHeapMaxSize + } + STATUS current + DESCRIPTION + "A collection of objects that are mapped from JSR 163 + java.lang.management.MemoryMXBean.getHeapMemoryUsage(). + When several of these objects are requested within a single + SNMP request, the agent must ensure that + java.lang.management.MemoryPoolMXBean.getHeapMemoryUsage() is + called only once, in order to guarantee that the set of + values returned for these objects remain coherent and give + a consistent snapshot of the heap memory usage made by + Heap Memory Pools. + " + ::= { jvmMemoryGroups 2 } + +jvmMemoryNonHeapUsageGroup OBJECT-GROUP + OBJECTS { + jvmMemoryNonHeapInitSize, + jvmMemoryNonHeapUsed, + jvmMemoryNonHeapCommitted, + jvmMemoryNonHeapMaxSize + } + STATUS current + DESCRIPTION + "A collection of objects that are mapped from JSR 163 + java.lang.management.MemoryMXBean.getNonHeapMemoryUsage(). + When several of these objects are requested within a single + SNMP request, the agent must ensure that + java.lang.management.MemoryPoolMXBean.getNonHeapMemoryUsage() is + called only once, in order to guarantee that the set of + values returned for these objects remain coherent and give + a consistent snapshot of the non heap memory usage made by + Non Heap Memory Pools. + " + ::= { jvmMemoryGroups 3 } + +jvmMemorySetGroup OBJECT-GROUP + OBJECTS { + jvmMemoryGCVerboseLevel, + jvmMemoryGCCall + } + STATUS current + DESCRIPTION + "A collection of writable scalar objects that are mapped from JSR 163 + java.lang.management.MemoryMXBean interface, and make it possible + to act on the Garbage Collector. Accessing these objects may + require special permissions - the agent implementation is + responsible for puting in place the appropriate access control + if needed. + " + ::= { jvmMemoryGroups 4 } + +jvmMemManagerGroup OBJECT-GROUP + OBJECTS { + jvmMemManagerName, + jvmMemManagerState + } + STATUS current + DESCRIPTION + "A collection of columnar objects that are mapped from JSR 163 + java.lang.management.MemoryManagerMXBean interface. + " + ::= { jvmMemoryGroups 5 } + +jvmMemGCGroup OBJECT-GROUP + OBJECTS { + jvmMemGCCount, + jvmMemGCTimeMs + } + STATUS current + DESCRIPTION + "A collection of columnar objects that are mapped from JSR 163 + java.lang.management.GarbageCollectorMXBean interface, and are + specific to GarbageCollector MXBeans. + These objects are used to model the inheritence link between + GarbageCollectorMXBean and its super interface - MemoryManagerMXBean. + " + ::= { jvmMemoryGroups 6 } + +jvmMemPoolGroups OBJECT IDENTIFIER ::= { jvmMemoryGroups 7 } + +jvmMemPoolBasicGroup OBJECT-GROUP + OBJECTS { + jvmMemPoolName, + jvmMemPoolType, + jvmMemPoolState, + jvmMemPoolPeakReset, + jvmMemPoolThreshdSupport, + jvmMemPoolCollectThreshdSupport + } + STATUS current + DESCRIPTION + "A collection of columnar objects that are mapped from JSR 163 + java.lang.management.MemoryPoolMXBean interface. + " + ::= { jvmMemPoolGroups 1 } + +jvmMemPoolMonitoringGroup OBJECT-GROUP + OBJECTS { + jvmMemPoolThreshold, + jvmMemPoolThreshdCount + } + STATUS current + DESCRIPTION + "Memory usage threshold objects mapped from + JSR 163 java.lang.management.MemoryPoolMXBean interface, which makes + it possible to configure low memory detection. + Accessing this object may require special permissions - the agent + implementation is responsible for puting in place the appropriate + access control if needed. + " + ::= { jvmMemPoolGroups 2 } + +jvmMemPoolUsageGroup OBJECT-GROUP + OBJECTS { + jvmMemPoolInitSize, + jvmMemPoolUsed, + jvmMemPoolCommitted, + jvmMemPoolMaxSize + } + STATUS current + DESCRIPTION + "A collection of objects that are mapped from JSR 163 + java.lang.management.MemoryPoolMXBean.getUsage(). + When several of these objects are requested within a single + SNMP request, the agent must ensure that + java.lang.management.MemoryPoolMXBean.getUsage() is + called only once, in order to guarantee that the set of + values returned for these objects remain coherent and give + a consistent snapshot of the memory used by this Memory + Pool. + " + ::= { jvmMemPoolGroups 3 } + +jvmMemPoolPeakUsageGroup OBJECT-GROUP + OBJECTS { + jvmMemPoolPeakUsed, + jvmMemPoolPeakCommitted, + jvmMemPoolPeakMaxSize + } + STATUS current + DESCRIPTION + "A collection of objects that are mapped from JSR 163 + java.lang.management.MemoryPoolMXBean.getPeakUsage(). + When several of these objects are requested within a single + SNMP request, the agent must ensure that + java.lang.management.MemoryPoolMXBean.getPeakUsage() is + called only once, in order to guarantee that the set of + values returned for these objects remain coherent and give + a consistent snapshot of the peak memory usage made by + this Memory Pool. + " + ::= { jvmMemPoolGroups 4 } + +jvmMemPoolCollectUsageGroup OBJECT-GROUP + OBJECTS { + jvmMemPoolCollectUsed, + jvmMemPoolCollectCommitted, + jvmMemPoolCollectMaxSize + } + STATUS current + DESCRIPTION + "A collection of objects that are mapped from JSR 163 + java.lang.management.MemoryPoolMXBean.getCollectionUsage(). + When several of these objects are requested within a single + SNMP request, the agent must ensure that + java.lang.management.MemoryPoolMXBean.getCollectionUsage() is + called only once, in order to guarantee that the set of + values returned for these objects remain coherent and give + a consistent snapshot of the collection memory usage made by + this Memory Pool. + " + ::= { jvmMemPoolGroups 5 } + +jvmMemPoolCollectMonitoringGroup OBJECT-GROUP + OBJECTS { + jvmMemPoolCollectThreshold, + jvmMemPoolCollectThreshdCount + } + STATUS current + DESCRIPTION + "Memory collection usage threshold objects mapped from JSR 163 + java.lang.management.MemoryPoolMXBean interface, which makes + it possible to configure low memory detection. + Accessing this object may require special permissions - the agent + implementation is responsible for putting in place the appropriate + access control if needed. + " + ::= { jvmMemPoolGroups 6 } + + +jvmMemMgrPoolRelationGroup OBJECT-GROUP + OBJECTS { + jvmMemMgrRelManagerName, + jvmMemMgrRelPoolName + } + STATUS current + DESCRIPTION + "A collection of columnar objects that are mapped from JSR 163 + java.lang.management.MemoryPoolMXBean and + java.lang.management.MemoryManagerMXBean interface, and show the + relationship between Memory Managers and Memory Pools. + " + ::= { jvmMemoryGroups 8 } + +jvmThreadGroups OBJECT IDENTIFIER ::= { jvmMgtMIBGroups 3 } + +jvmThreadBasicGroup OBJECT-GROUP + OBJECTS { + jvmThreadCount, + jvmThreadDaemonCount, + jvmThreadPeakCount, + jvmThreadTotalStartedCount, + jvmThreadContentionMonitoring, + jvmThreadCpuTimeMonitoring, + jvmThreadPeakCountReset + } + STATUS current + DESCRIPTION + "A collection of scalar objects that are mapped from JSR 163 + java.lang.management.ThreadMXBean interface. + " + ::= { jvmThreadGroups 1 } + +jvmThreadInstanceGroups OBJECT IDENTIFIER ::= { jvmThreadGroups 2 } + +jvmThreadInstanceBasicGroup OBJECT-GROUP + OBJECTS { + jvmThreadInstId, + jvmThreadInstState, + jvmThreadInstName, + jvmThreadInstLockName, + jvmThreadInstLockOwnerPtr + } + STATUS current + DESCRIPTION + "A collection of columnar objects that are mapped from JSR 163 + java.lang.management.ThreadMXBean interface, and are + relative to an instance of java.lang.Thread. + " + ::= { jvmThreadInstanceGroups 1} + +jvmThreadInstanceCpuGroup OBJECT-GROUP + OBJECTS { + jvmThreadInstCpuTimeNs + } + STATUS current + DESCRIPTION + "A columnar object mapped from JSR 163 + java.lang.management.ThreadMXBean interface which provides CPU + time statistics about an instance of java.lang.Thread. + " + ::= { jvmThreadInstanceGroups 2 } + + +jvmThreadInstanceBlockGroup OBJECT-GROUP + OBJECTS { + jvmThreadInstBlockCount, + jvmThreadInstBlockTimeMs, + jvmThreadInstWaitCount, + jvmThreadInstWaitTimeMs + } + STATUS current + DESCRIPTION + "A collection of columnar objects that are mapped from JSR 163 + java.lang.management.ThreadMXBean interface, and which provide + synchronization statistics about an instance of java.lang.Thread. + " + ::= { jvmThreadInstanceGroups 3 } + + +jvmRuntimeGroups OBJECT IDENTIFIER ::= { jvmMgtMIBGroups 4 } + +jvmRuntimeBasicGroup OBJECT-GROUP + OBJECTS { + jvmRTName, + jvmRTVMName, + jvmRTVMVendor, + jvmRTVMVersion, + jvmRTSpecName, + jvmRTSpecVendor, + jvmRTSpecVersion, + jvmRTManagementSpecVersion, + jvmRTUptimeMs, + jvmRTStartTimeMs, + jvmRTBootClassPathSupport, + jvmRTInputArgsCount, + jvmRTInputArgsItem, + jvmRTClassPathItem, + jvmRTLibraryPathItem + } + STATUS current + DESCRIPTION + "A collection of objects that are mapped from JSR 163 + java.lang.management.RuntimeMXBean interface. + " + ::= { jvmRuntimeGroups 1 } + + +jvmRuntimeBootCPGroup OBJECT-GROUP + OBJECTS { + jvmRTBootClassPathItem + } + STATUS current + DESCRIPTION + "A columnar object that is mapped from JSR 163 + java.lang.management.RuntimeMXBean.getBootClassPath() interface, + and provide information about bootclasspath elements. + " + ::= { jvmRuntimeGroups 2 } + +jvmJITCompilerGroups OBJECT IDENTIFIER ::= { jvmMgtMIBGroups 5 } + +jvmJITCompilerBasicGroup OBJECT-GROUP + OBJECTS { + jvmJITCompilerName, + jvmJITCompilerTimeMonitoring + } + STATUS current + DESCRIPTION + "A collection of objects that are mapped from JSR 163 + java.lang.management.CompilationMXBean interface. + " + ::= { jvmJITCompilerGroups 1 } + +jvmJITCompilerTimeStatGroup OBJECT-GROUP + OBJECTS { + jvmJITCompilerTimeMs + } + STATUS current + DESCRIPTION + "A collection of objects that are mapped from JSR 163 + java.lang.management.CompilationMXBean interface and provide + time statistic about the JIT Compiler. + " + ::= { jvmJITCompilerGroups 2 } + +jvmOSGroup OBJECT-GROUP + OBJECTS { + jvmOSName, + jvmOSArch, + jvmOSVersion, + jvmOSProcessorCount + } + STATUS current + DESCRIPTION + "A collection of objects that are mapped from JSR 163 + java.lang.management.OperatingSystemMXBean interface. + " + ::= { jvmMgtMIBGroups 6 } + +jvmLowMemoryUsageNotifGroup NOTIFICATION-GROUP + NOTIFICATIONS { + jvmLowMemoryPoolUsageNotif + } + STATUS current + DESCRIPTION + "A collection of notifications emitted when low + memory usage conditions are detected. + " + ::= { jvmMgtMIBGroups 7 } + +jvmLowMemoryCollectNotifGroup NOTIFICATION-GROUP + NOTIFICATIONS { + jvmLowMemoryPoolCollectNotif + } + STATUS current + DESCRIPTION + "A collection of notifications emitted when low + collection memory usage conditions are detected. + " + ::= { jvmMgtMIBGroups 8 } + +END diff --git a/tomcat6/files/default/dtomcat6 b/tomcat6/files/default/dtomcat6 new file mode 100755 index 0000000..e79fd5e --- /dev/null +++ b/tomcat6/files/default/dtomcat6 @@ -0,0 +1,81 @@ +#!/bin/bash + +# Get the tomcat config (use this for environment specific settings) +if [ -z "${TOMCAT_CFG}" ]; then + TOMCAT_CFG="/etc/tomcat6/tomcat6.conf" +fi + +if [ -r "$TOMCAT_CFG" ]; then + . $TOMCAT_CFG +fi + +# CLASSPATH munging +if [ -n "$JSSE_HOME" ]; then + CLASSPATH="${CLASSPATH}:$(build-classpath jcert jnet jsse 2>/dev/null)" +fi +CLASSPATH="${CLASSPATH}:${CATALINA_HOME}/bin/bootstrap.jar" +CLASSPATH="${CLASSPATH}:${CATALINA_HOME}/bin/tomcat-juli.jar" +CLASSPATH="${CLASSPATH}:$(build-classpath commons-daemon 2>/dev/null)" + +if [ "$1" = "start" ]; then + ${JAVA_HOME}/bin/java $JAVA_OPTS $CATALINA_OPTS \ + -classpath "$CLASSPATH" \ + -Dcatalina.base="$CATALINA_BASE" \ + -Dcatalina.home="$CATALINA_HOME" \ + -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" \ + -Djava.io.tmpdir="$CATALINA_TMPDIR" \ + -Djava.util.logging.config.file="${CATALINA_BASE}/conf/logging.properties" \ + -Djava.util.logging.manager="org.apache.juli.ClassLoaderLogManager" \ + org.apache.catalina.startup.Bootstrap start \ + >> ${CATALINA_BASE}/logs/catalina.out 2>&1 & + if [ ! -z "$CATALINA_PID" ]; then + echo $! > $CATALINA_PID + fi +elif [ "$1" = "start-security" ]; then + ${JAVA_HOME}/bin/java $JAVA_OPTS $CATALINA_OPTS \ + -classpath "$CLASSPATH" \ + -Dcatalina.base="$CATALINA_BASE" \ + -Dcatalina.home="$CATALINA_HOME" \ + -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" \ + -Djava.io.tmpdir="$CATALINA_TMPDIR" \ + -Djava.security.manager \ + -Djava.security.policy="${CATALINA_BASE}/conf/catalina.policy" \ + -Djava.util.logging.config.file="${CATALINA_BASE}/conf/logging.properties" \ + -Djava.util.logging.manager="org.apache.juli.ClassLoaderLogManager" \ + org.apache.catalina.startup.Bootstrap start \ + >> ${CATALINA_BASE}/logs/catalina.out 2>&1 & + if [ ! -z "$CATALINA_PID" ]; then + echo $! > $CATALINA_PID + fi +elif [ "$1" = "start-jdpa" ]; then + ${JAVA_HOME}/bin/java $JAVA_OPTS $CATALINA_OPTS \ + -agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n\ + -classpath "$CLASSPATH" \ + -Dcatalina.base="$CATALINA_BASE" \ + -Dcatalina.home="$CATALINA_HOME" \ + -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" \ + -Djava.io.tmpdir="$CATALINA_TMPDIR" \ + -Djava.util.logging.config.file="${CATALINA_BASE}/conf/logging.properties" \ + -Djava.util.logging.manager="org.apache.juli.ClassLoaderLogManager" \ + org.apache.catalina.startup.Bootstrap start \ + >> ${CATALINA_BASE}/logs/catalina.out 2>&1 & + if [ ! -z "$CATALINA_PID" ]; then + echo $! > $CATALINA_PID + fi +elif [ "$1" = "stop" ]; then + ${JAVA_HOME}/bin/java $CATALINA_OPTS \ + -classpath "$CLASSPATH" \ + -Dcatalina.base="$CATALINA_BASE" \ + -Dcatalina.home="$CATALINA_HOME" \ + -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" \ + -Djava.io.tmpdir="$CATALINA_TMPDIR" \ + org.apache.catalina.startup.Bootstrap stop \ + >> ${CATALINA_BASE}/logs/catalina.out 2>&1 +elif [ "$1" = "version" ]; then + ${JAVA_HOME}/bin/java -classpath ${CATALINA_HOME}/lib/catalina.jar \ + org.apache.catalina.util.ServerInfo +else + echo "Usage: $0 {start|start-security|stop|version}" + exit 1 +fi + diff --git a/tomcat6/files/default/logging.properties b/tomcat6/files/default/logging.properties new file mode 100644 index 0000000..f9bdd00 --- /dev/null +++ b/tomcat6/files/default/logging.properties @@ -0,0 +1,73 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. + +#handlers = 1catalina.org.apache.juli.FileHandler, 2localhost.org.apache.juli.FileHandler, 3manager.org.apache.juli.FileHandler, 4admin.org.apache.juli.FileHandler, 5host-manager.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler +handlers = java.util.logging.ConsoleHandler + +#.handlers = 1catalina.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler +.handlers = java.util.logging.ConsoleHandler + +############################################################# +## Handler specific properties. +## Describes specific configuration info for Handlers. +############################################################# + +#1catalina.org.apache.juli.FileHandler.level = FINE +#1catalina.org.apache.juli.FileHandler.directory = ${catalina.base}/logs +#1catalina.org.apache.juli.FileHandler.prefix = catalina. + +#2localhost.org.apache.juli.FileHandler.level = FINE +#2localhost.org.apache.juli.FileHandler.directory = ${catalina.base}/logs +#2localhost.org.apache.juli.FileHandler.prefix = localhost. + +#3manager.org.apache.juli.FileHandler.level = FINE +#3manager.org.apache.juli.FileHandler.directory = ${catalina.base}/logs +#3manager.org.apache.juli.FileHandler.prefix = manager. + +#4admin.org.apache.juli.FileHandler.level = FINE +#4admin.org.apache.juli.FileHandler.directory = ${catalina.base}/logs +#4admin.org.apache.juli.FileHandler.prefix = admin. + +#5host-manager.org.apache.juli.FileHandler.level = FINE +#5host-manager.org.apache.juli.FileHandler.directory = ${catalina.base}/logs +#5host-manager.org.apache.juli.FileHandler.prefix = host-manager. + +java.util.logging.ConsoleHandler.level = INFO +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter + + +############################################################# +## Facility specific properties. +## Provides extra control for each logger. +############################################################# + +org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO +org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = java.util.logging.ConsoleHandler + +#org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].level = INFO +#org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].handlers = 3manager.org.apache.juli.FileHandler + +#org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/admin].level = INFO +#org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/admin].handlers = 4admin.org.apache.juli.FileHandler + +#org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].level = INFO +#org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].handlers = 5host-manager.org.apache.juli.FileHandler + +## For example, set the com.xyz.foo logger to only log SEVERE +## messages: +##org.apache.catalina.startup.ContextConfig.level = FINE +##org.apache.catalina.startup.HostConfig.level = FINE +##org.apache.catalina.session.ManagerBase.level = FINE +##org.apache.catalina.core.AprLifecycleListener.level=FINE \ No newline at end of file diff --git a/tomcat6/files/default/tomcat6 b/tomcat6/files/default/tomcat6 new file mode 100644 index 0000000..e39819e --- /dev/null +++ b/tomcat6/files/default/tomcat6 @@ -0,0 +1,321 @@ +#!/bin/bash +# +# tomcat6 This shell script takes care of starting and stopping Tomcat +# +# chkconfig: - 80 20 +# +### BEGIN INIT INFO +# Provides: tomcat6 +# Required-Start: $network $syslog +# Required-Stop: $network $syslog +# Default-Start: +# Default-Stop: +# Description: Release implementation for Servlet 2.5 and JSP 2.1 +# Short-Description: start and stop tomcat +### END INIT INFO +# +# - originally written by Henri Gomez, Keith Irwin, and Nicolas Mailhot +# - heavily rewritten by Deepak Bhole and Jason Corley +# + +ulimit -n 4096 + +# set a minimalist PATH +PATH="/bin:/sbin" + +NAME="$(basename $0)" +unset ISBOOT +if [ "${NAME:0:1}" = "S" -o "${NAME:0:1}" = "K" ]; then + NAME="${NAME:3}" + ISBOOT="1" +fi + +# For SELinux we need to use 'runuser' not 'su' +if [ -x "/sbin/runuser" ]; then + SU="/sbin/runuser" +else + SU="/bin/su" +fi + +# Get the tomcat config (use this for environment specific settings) +TOMCAT_CFG="/etc/tomcat6/tomcat6.conf" +if [ -r "$TOMCAT_CFG" ]; then + . $TOMCAT_CFG +fi + +# Get instance specific config file +if [ -r "/etc/sysconfig/${NAME}" ]; then + . /etc/sysconfig/${NAME} +fi + +# Define which connector port to use +CONNECTOR_PORT="${CONNECTOR_PORT:-8080}" + +# Path to the tomcat launch script +TOMCAT_SCRIPT="/usr/bin/dtomcat6" + +# Tomcat program name +TOMCAT_PROG="$NAME" + +# Define the tomcat username +TOMCAT_USER="${TOMCAT_USER:-tomcat}" + +# Define the tomcat log file +TOMCAT_LOG="${TOMCAT_LOG:-/var/log/tomcat6/catalina.out}" + +touch $TOMCAT_LOG +chown ${TOMCAT_USER}:${TOMCAT_USER} $TOMCAT_LOG + +RETVAL="0" + +# pulled from RHEL4 /etc/rc.d/init.d/functions +function checkpid() { + local i + for i in $* ; do + if [ -d "/proc/${i}" ]; then + return 0 + fi + done + return 1 +} + +# pulled from RHEL4 /etc/rc.d/init.d/functions +function echo_failure() { + echo -en "\\033[60G" + echo -n "[ " + echo -n $"FAILED" + echo -n " ]" + echo -ne "\r" + return 1 +} + +# pulled from RHEL4 /etc/rc.d/init.d/functions +function echo_success() { + echo -en "\\033[60G" + echo -n "[ " + echo -n $"OK" + echo -n " ]" + echo -ne "\r" + return 0 +} + +# Look for open ports, as the function name might imply +function findFreePorts() { + local isSet1="false" + local isSet2="false" + local isSet3="false" + local lower="8000" + randomPort1="0" + randomPort2="0" + randomPort3="0" + local -a listeners="( $( + netstat -ntl | \ + awk '/^tcp/ {gsub("(.)*:", "", $4); print $4}' + ) )" + while [ "$isSet1" = "false" ] || \ + [ "$isSet2" = "false" ] || \ + [ "$isSet3" = "false" ]; do + let port="${lower}+${RANDOM:0:4}" + if [ -z `expr " ${listeners[*]} " : ".*\( $port \).*"` ]; then + if [ "$isSet1" = "false" ]; then + export randomPort1="$port" + isSet1="true" + elif [ "$isSet2" = "false" ]; then + export randomPort2="$port" + isSet2="true" + elif [ "$isSet3" = "false" ]; then + export randomPort3="$port" + isSet3="true" + fi + fi + done +} + +function makeHomeDir() { + if [ ! -d "$CATALINA_HOME" ]; then + echo "$CATALINA_HOME does not exist, creating" + if [ ! -d "/usr/share/${NAME}" ]; then + mkdir /usr/share/${NAME} + cp -pLR /usr/share/tomcat6/* /usr/share/${NAME} + fi + mkdir -p /var/log/${NAME} \ + /var/cache/${NAME} \ + /var/tmp/${NAME} + ln -fs /var/cache/${NAME} ${CATALINA_HOME}/work + ln -fs /var/tmp/${NAME} ${CATALINA_HOME}/temp + cp -pLR /usr/share/${NAME}/bin $CATALINA_HOME + cp -pLR /usr/share/${NAME}/conf $CATALINA_HOME + ln -fs /usr/share/java/tomcat6 ${CATALINA_HOME}/lib + ln -fs /usr/share/tomcat6/webapps ${CATALINA_HOME}/webapps + chown ${TOMCAT_USER}:${TOMCAT_USER} /var/log/${NAME} + fi +} + +function parseOptions() { + options="" + options="$options $( + awk '!/^#/ && !/^$/ { ORS=" "; print "export ", $0, ";" }' \ + $TOMCAT_CFG + )" + if [ -r "/etc/sysconfig/${NAME}" ]; then + options="$options $( + awk '!/^#/ && !/^$/ { ORS=" "; + print "export ", $0, ";" }' \ + /etc/sysconfig/${NAME} + )" + fi + TOMCAT_SCRIPT="$options $TOMCAT_SCRIPT" +} + +# See how we were called. +function start() { + echo -n "Starting ${TOMCAT_PROG}: " + if [ -f "/var/lock/subsys/${NAME}" ] ; then + if [ -f "/var/run/${NAME}.pid" ]; then + read kpid < /var/run/${NAME}.pid + if checkpid $kpid 2>&1; then + echo "$NAME process already running" + return -1 + else + echo "lock file found but no process running for" + echo "pid $kpid, continuing" + fi + fi + fi + # fix permissions on the log and pid files + export CATALINA_PID="/var/run/${NAME}.pid" + touch $CATALINA_PID + chown ${TOMCAT_USER}:${TOMCAT_USER} $CATALINA_PID + touch $TOMCAT_LOG + chown ${TOMCAT_USER}:${TOMCAT_USER} $TOMCAT_LOG + if [ "$CATALINA_HOME" != "/usr/share/tomcat6" ]; then + # Create a tomcat directory if it doesn't exist + makeHomeDir + # If CATALINA_HOME doesn't exist modify port number so that + # multiple instances don't interfere with each other + findFreePorts + sed -i -e "s/8005/${randomPort1}/g" -e "s/8080/${CONNECTOR_PORT}/g" \ + -e "s/8009/${randomPort2}/g" -e "s/8443/${randomPort3}/g" \ + ${CATALINA_HOME}/conf/server.xml + fi + if [ "$JDPA" = "true" ]; then + START="start-jdpa" + else + START="start" + fi + if [ "$SECURITY_MANAGER" = "true" ]; then + $SU - $TOMCAT_USER -c "$TOMCAT_SCRIPT start-security" \ + >> $TOMCAT_LOG 2>&1 + else + $SU - $TOMCAT_USER -c "$TOMCAT_SCRIPT $START" >> $TOMCAT_LOG 2>&1 + fi + RETVAL="$?" + if [ "$RETVAL" -eq 0 ]; then + echo_success + touch /var/lock/subsys/${NAME} + else + echo_failure + fi + echo + return $RETVAL +} + +function status() { + RETVAL="1" + if [ -f "/var/run/${NAME}.pid" ]; then + read kpid < /var/run/${NAME}.pid + if checkpid $kpid 2>&1; then + echo "$0 is already running (${kpid})" + RETVAL="0" + else + echo "lock file found but no process running for pid $kpid" + fi + else + pid="$(/usr/bin/pgrep -u tomcat java)" + if [ -n "$pid" ]; then + echo "$0 running (${pid}) but no PID file exists" + RETVAL="0" + else + echo "$0 is stopped" + fi + fi + return $RETVAL +} + +function stop() { + echo -n "Stopping $TOMCAT_PROG: " + if [ -f "/var/lock/subsys/${NAME}" ]; then + $SU - $TOMCAT_USER -c "$TOMCAT_SCRIPT stop" >> $TOMCAT_LOG 2>&1 + RETVAL="$?" + if [ "$RETVAL" -eq "0" ]; then + count="0" + if [ -f "/var/run/${NAME}.pid" ]; then + read kpid < /var/run/${NAME}.pid + until [ "$(ps --pid $kpid | grep -c $kpid)" -eq "0" ] || \ + [ "$count" -gt "$SHUTDOWN_WAIT" ]; do + if [ "$SHUTDOWN_VERBOSE" = "true" ]; then + echo -n -e "\nwaiting for processes $kpid to exit" + fi + sleep 1 + let count="${count}+1" + done + if [ "$count" -gt "$SHUTDOWN_WAIT" ]; then + if [ "$SHUTDOWN_VERBOSE" = "true" ]; then + echo -n -e "\nkilling processes which didn't stop" + echo -n -e "after " + echo -n "$SHUTDOWN_WAIT seconds" + fi + kill -9 $kpid + fi + echo_success + if [ "$count" -gt "0" ]; then + echo -n -e "\n" + fi + fi + rm -f /var/lock/subsys/${NAME} /var/run/${NAME}.pid + else + echo_failure + fi + fi +} + + +# See how we were called. +case "$1" in + start) + parseOptions + start + ;; + start-jdpa) + parseOptions + JDPA="true" + start + ;; + stop) + parseOptions + stop + ;; + restart) + parseOptions + stop + sleep 2 + start + ;; + condrestart) + if [ -f "/var/run/${NAME}.pid" ]; then + restart + fi + ;; + status) + status + ;; + version) + $TOMCAT_SCRIPT version + ;; + *) + echo -n "Usage: $TOMCAT_PROG " + echo "{start|start-jdpa|stop|restart|condrestart|status|version}" + exit 1 +esac + +exit $RETVAL diff --git a/tomcat6/libraries/tomcat.rb b/tomcat6/libraries/tomcat.rb new file mode 100644 index 0000000..cfe6cf8 --- /dev/null +++ b/tomcat6/libraries/tomcat.rb @@ -0,0 +1,58 @@ +require 'net/http' +require 'net/https' + +class Tomcat + # wrapper arount tomcat manager http api + + def initialize(opts={}) + @configuration = opts + end + + def configuration + @configuration + end + + def install + tag_param = (@configuration[:tag])?"&tag=#{@configuration[:tag]}":"" + get("/manager/deploy?path=#{@configuration[:path]}&war=file:#{@configuration[:war]}") + end + + def update + tag_param = (@configuration[:tag])?"&tag=#{@configuration[:tag]}":"" + get("/manager/deploy?path=#{@configuration[:path]}&war=file:#{@configuration[:war]}&update=true") + end + + def undeploy + get("/manager/undeploy?path=#{@configuration[:path]}") + end + + def reload + get("/manager/reload?path=#{@configuration[:path]}") + end + + def start + get("/manager/start?path=#{@configuration[:path]}") + end + + def stop + get("/manager/stop?path=#{@configuration[:path]}") + end + + def status + get("/manager/status") + end + + def get(url) + site = Net::HTTP.new(@configuration[:host], @configuration[:port]) + site.use_ssl = false + site.read_timeout=180 + result = nil + begin + result = site.get2( url, 'Authorization' => 'Basic ' + ["#{@configuration[:admin]}:#{@configuration[:password]}"].pack('m').strip) + rescue Timeout::Error => e + raise RuntimeError, "Timeout Error while calling #{url}", caller + end + result + end + +end diff --git a/tomcat6/libraries/tomcat_manager.rb b/tomcat6/libraries/tomcat_manager.rb new file mode 100644 index 0000000..a2bd7a2 --- /dev/null +++ b/tomcat6/libraries/tomcat_manager.rb @@ -0,0 +1,233 @@ + +require File.join(File.dirname(__FILE__), 'tomcat') + +class Chef + class Resource + class TomcatManager < Chef::Resource + + def initialize(name, collection=nil, node=nil) + super(name, collection, node) + @resource_name = :tomcat_manager + @host = "localhost" + @port = "8080" + @action = :nothing + @allowed_actions.push(:install) + @allowed_actions.push(:start) + @allowed_actions.push(:stop) + @allowed_actions.push(:update) + @allowed_actions.push(:undeploy) + + end + + def port(arg=nil) + set_or_return( + :port, + arg, + :kind_of => [ String ] + ) + end + + def host(arg=nil) + set_or_return( + :host, + arg, + :kind_of => [ String ] + ) + end + + def admin(arg=nil) + set_or_return( + :admin, + arg, + :kind_of => [ String ] + ) + end + + def password(arg=nil) + set_or_return( + :password, + arg, + :kind_of => [ String ] + ) + end + + def base(arg=nil) + set_or_return( + :base, + arg, + :kind_of => [ String ] + ) + end + + def war(arg=nil) + set_or_return( + :war, + arg, + :kind_of => [ String ] + ) + end + + def path(arg=nil) + set_or_return( + :path, + arg, + :kind_of => [ String ] + ) + end + + def tag(arg=nil) + set_or_return( + :tag, + arg, + :kind_of => [ String ] + ) + end + + def service(arg=nil) + set_or_return( + :service, + arg, + :kind_of => [ Object ] + ) + end + + + + end + end + + class Provider + class TomcatManager < Chef::Provider + + def load_current_resource + #super + #@current_resource + end + + def action_install + ensure_tomcat_manager_running + Chef::Log.info "Running tomcat_manager[#{@new_resource.name}] install" + tomcat = new_tomcat + begin + result = tomcat.install + (!"200".eql?(result.code) || result.body.include?("FAIL"))?(Chef::Log.error "Ran tomcat_manager[#{@new_resource.name}] install failed: HTTP #{result.code} #{result.message}"):(Chef::Log.info "Ran tomcat_manager[#{@new_resource.name}] install successfully") + rescue StandardError => e + Chef::Log.error "Got Exception in manager action" + e + end + end + + def action_update + ensure_tomcat_manager_running + Chef::Log.info "Running tomcat_manager[#{@new_resource.name}] update" + tomcat = new_tomcat + begin + result = tomcat.update + (!"200".eql?(result.code) || result.body.include?("FAIL"))?(Chef::Log.error "Ran tomcat_manager[#{@new_resource.name}] update failed: HTTP #{result.code} #{result.message}"):(Chef::Log.info "Ran tomcat_manager[#{@new_resource.name}] update successfully") + rescue StandardError => e + Chef::Log.error "Got Exception in manager action" + e + end + end + + def action_start + ensure_tomcat_manager_running + Chef::Log.info "Running tomcat_manager[#{@new_resource.name}] start" + tomcat = new_tomcat + begin + result = tomcat.start + (!"200".eql?(result.code) || result.body.include?("FAIL"))?(Chef::Log.error "Ran tomcat_manager[#{@new_resource.name}] start failed: HTTP #{result.code} #{result.message}"):(Chef::Log.info "Ran tomcat_manager[#{@new_resource.name}] start successfully") + rescue StandardError => e + Chef::Log.error "Got Exception in manager action" + e + end + end + + def action_stop + ensure_tomcat_manager_running + Chef::Log.info "Running tomcat_manager[#{@new_resource.name}] stop" + tomcat = new_tomcat + begin + result = tomcat.stop + (!"200".eql?(result.code) || result.body.include?("FAIL"))?(Chef::Log.error "Ran tomcat_manager[#{@new_resource.name}] stop failed: HTTP #{result.code} #{result.message}"):(Chef::Log.info "Ran tomcat_manager[#{@new_resource.name}] stop successfully") + rescue StandardError => e + Chef::Log.error "Got Exception in manager action" + e + end + end + + def action_undeploy + ensure_tomcat_manager_running + Chef::Log.info "Running tomcat_manager[#{@new_resource.name}] undeploy" + tomcat = new_tomcat + begin + result = tomcat.undeploy + (!"200".eql?(result.code) || result.body.include?("FAIL"))?(Chef::Log.error "Ran tomcat_manager[#{@new_resource.name}] undeploy failed: HTTP #{result.code} #{result.message}"):(Chef::Log.info "Ran tomcat_manager[#{@new_resource.name}] undeploy successfully") + rescue StandardError => e + Chef::Log.error "Got Exception in manager action" + e + end + end + + def new_tomcat(opts={}) + Tomcat.new :host => @new_resource.host, + :port => @new_resource.port, + :admin => @new_resource.admin, + :password => @new_resource.password, + :path => @new_resource.path, + :base => @new_resource.base, + :war => @new_resource.war, + :name => @new_resource.name, + :tag => @new_resource.tag + + end + + def ensure_tomcat_manager_running + require 'snmp' + + snmp = Hash.new + # "jvmMemPoolUsed.5" is PermGen used + snmp_names = ["jvmMemPoolUsed.5","jvmMemPoolMaxSize.5"] + begin + SNMP::Manager.open(:Host => "localhost", :Port => "1161", :MibModules => ['JVM-MANAGEMENT-MIB','SNMPv2-SMI']) do |m| + response = m.get(snmp_names) + i=0 + response.each_varbind do |vb| + snmp[snmp_names[i]]=vb + i+=1 + end + end + rescue + Chef::Log.info "SNMP java not responding, restarting [#{@new_resource.name}] " + #restart passed in service, which should be the tomcat service + @new_resource.service.run_action(:restart) + poll_manger_until_running + end + perm_gen_max = (snmp["jvmMemPoolMaxSize.5"])?(snmp["jvmMemPoolMaxSize.5"].value.to_f):27262976 + perm_gen_used = (snmp["jvmMemPoolUsed.5"])?(snmp["jvmMemPoolUsed.5"].value.to_f):0 + #restart passed in service, which should be the tomcat service + min_perm_gen = 25*1024*1024 + if ((perm_gen_max - perm_gen_used) < min_perm_gen) #@node[:tomcat][:permgen_min_free_in_mb]) + Chef::Log.info "SNMP reported: PermGen Max:#{perm_gen_max/1024/1024} PermGen Used:#{perm_gen_used/1024/1024} , diff: #{(perm_gen_max/1024/1024 - perm_gen_used/1024/1024)} is lower than #{min_perm_gen/1024/1024} " + @new_resource.service.run_action(:restart) + poll_manger_until_running + end + end + def poll_manger_until_running + times = 40 + tomcat = new_tomcat + while(times > 0) + Chef::Log.info "waiting for tomcat_manager[#{@new_resource.name}] restart" + begin + tomcat.status + times = 0 + rescue Timeout::Error + Chef::Log.info "Timeout, waiting for another #{2*times} seconds" + rescue + Chef::Log.info "Manager not responding, waiting for another #{2*times} seconds" + ensure + sleep 2 + times-=1 + end + end + end + end + end +end + +Chef::Platform.platforms[:default].merge! :tomcat_manager => Chef::Provider::TomcatManager diff --git a/tomcat6/metadata.json b/tomcat6/metadata.json new file mode 100644 index 0000000..176e0dc --- /dev/null +++ b/tomcat6/metadata.json @@ -0,0 +1,60 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs and configures all aspects of tomcat6 using custom local installation", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "tomcat6": "Main Tomcat 6 configuration", + "tomcat6": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "centos": [ + + ], + "debian": [ + + ], + "redhat": [ + + ] + }, + "version": "0.1.0", + "name": "tomcat6", + "conflicting": { + + }, + "attributes": { + "tomcat6\/with_native": { + "default": "false", + "type": "string", + "multiple_values": false, + "description": "works for centos, install tomcat-native libraries", + "display_name": "Tomcat native support", + "recipes": [ + + ], + "required": false + } + }, + "providing": { + "tomcat6": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "= DESCRIPTION:\n\nInstalls Tomcat6\n\n= REQUIREMENTS:\n\n== Platform and Application Environment:\n\nTested on Centos 5.2 8.10. May work on other platforms, esp Redhat.\nNeeds Java at least Java 5\n\n== Cookbooks:\n\nOpscode cookbooks, http:\/\/github.com\/opscode\/cookbooks\/tree\/master:\n\n* java\n\n= ATTRIBUTES: \n\n= USAGE:\n\n\n= LICENSE and AUTHOR:\n \nAuthor:: Edmund Haselwanter ()\nCopyright:: 2009, Edmund Haselwanter\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/tomcat6/metadata.rb b/tomcat6/metadata.rb new file mode 100644 index 0000000..bc6dc51 --- /dev/null +++ b/tomcat6/metadata.rb @@ -0,0 +1,17 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures all aspects of tomcat6 using custom local installation" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.1" +recipe "tomcat6", "Main Tomcat 6 configuration" + +%w{redhat centos debian ubuntu}.each do |os| + supports os +end + +attribute "tomcat6/with_native", + :display_name => "Tomcat native support", + :description => "works for centos, install tomcat-native libraries", + :type => "string", + :default => "false" diff --git a/tomcat6/recipes/default.rb b/tomcat6/recipes/default.rb new file mode 100644 index 0000000..7f577f6 --- /dev/null +++ b/tomcat6/recipes/default.rb @@ -0,0 +1,197 @@ +# +# Cookbook Name:: tomcat6 +# Recipe:: default +# +# Copyright 2009, Edmund Haselwanter +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +#include_recipe "java" + +service "tomcat6" do + action :nothing +end + +group node[:tomcat6][:user] do +end + +user node[:tomcat6][:user] do + comment "Apache Tomcat" + gid node[:tomcat6][:user] + home node[:tomcat6][:home] + shell "/bin/sh" +end + +[node[:tomcat6][:temp],node[:tomcat6][:logs],node[:tomcat6][:webapps],node[:tomcat6][:home],node[:tomcat6][:conf]].each do |dir| + directory dir do + action :create + mode 0755 + owner "#{node[:tomcat6][:user]}" + group "#{node[:tomcat6][:user]}" + end +end + +[:temp,:logs,:webapps,:conf].each do |dir| + link File.join(node[:tomcat6][:home],dir.to_s) do + to node[:tomcat6][dir] # use values from attributes + end +end + +usr_share_dir = "/usr/share" + +bash "update_manager" do + user node[:tomcat6][:user] + action :nothing + cwd node[:tomcat6][:webapps] + code <<-EOH + rm -rf ./manager + cp -r #{usr_share_dir}/apache-tomcat-#{node[:tomcat6][:version]}/webapps/manager . + EOH +end + +bash "install_tomcat6" do + tomcat_version_name = "apache-tomcat-#{node[:tomcat6][:version]}" + tomcat_version_name_tgz = "#{tomcat_version_name}.tar.gz" + user "root" + cwd usr_share_dir + not_if do File.exists?(File.join(usr_share_dir,tomcat_version_name)) end + code <<-EOH + wget http://archive.apache.org/dist/tomcat/tomcat-6/v#{node[:tomcat6][:version]}/bin/#{tomcat_version_name_tgz} + tar -zxf #{tomcat_version_name_tgz} + rm #{tomcat_version_name_tgz} + chown -R #{node[:tomcat6][:user]}:#{node[:tomcat6][:user]} #{tomcat_version_name} + EOH +end + +# just to have it here, may be overriden through own configuration +bash "install_tomcat6_etc" do + user node[:tomcat6][:user] + not_if do File.exists?(File.join(node[:tomcat6][:conf],"tomcat6.conf")) end + cwd node[:tomcat6][:conf] + code <<-EOH + cp -r #{usr_share_dir}/apache-tomcat-#{node[:tomcat6][:version]}/conf/* . + EOH +end + +link File.join(node[:tomcat6][:home],"lib") do + to File.join(usr_share_dir,"apache-tomcat-#{node[:tomcat6][:version]}","lib") + notifies :run, resources(:bash => "update_manager"), :immediately + notifies :restart, resources(:service => "tomcat6"), :delayed +end + +link File.join(node[:tomcat6][:home],"bin") do + to File.join(usr_share_dir,"apache-tomcat-#{node[:tomcat6][:version]}","bin") + notifies :restart, resources(:service => "tomcat6"), :delayed +end + +case node[:platform] +when "centos" + + # remote_file "/etc/yum.repos.d/rightscale.repo" do + # source "rightscale.repo" + # mode 0755 + # owner "root" + # group "root" + # end + # + # package "tomcat6" + # + # link "/usr/share/java/tomcat6/\[ecj\].jar" do + # to "/usr/share/java/eclipse-ecj.jar" + # end + # + + # package "tomcat6-admin-webapps" + + r = remote_file "/tmp/JVM-MANAGEMENT-MIB.mib" do + source "JVM-MANAGEMENT-MIB.mib" + mode 0755 + owner "root" + group "root" + end + + r.run_action(:create) + + p = package "libsmi" do + action :install + end + + p.run_action(:install) + + g = gem_package "snmp" do + action :install + end + + g.run_action(:install) + + Gem.clear_paths + + require "snmp" + + SNMP::MIB.import_module("/tmp/JVM-MANAGEMENT-MIB.mib") + + package "tomcat-native" do + action :install + only_if do @node[:tomcat6][:with_native] end + end + + +else + +end + +remote_file "/etc/init.d/tomcat6" do + source "tomcat6" + mode 0755 + owner "root" + group "root" +end + +remote_file "/usr/bin/dtomcat6" do + source "dtomcat6" + mode 0755 + owner "root" + group "root" +end + +remote_file File.join(node[:tomcat6][:dir],"logging.properties") do + source "logging.properties" + mode 0644 + owner "root" + group "root" +end + +service "tomcat6" do + case node[:platform] + when "centos" + service_name "tomcat6" + else + name "tomcat" + end + supports :start=> true, :stop => true, :restart => true, :status => true + action :enable +end + +template "#{node[:tomcat6][:dir]}/tomcat6.conf" do + source "tomcat6.conf.erb" + group "#{node[:tomcat6][:user]}" + owner "#{node[:tomcat6][:user]}" + mode 0644 + notifies :stop, resources(:service => "god"), :immediately + notifies :restart, resources(:service => "tomcat6"), :immediately +end + +service "tomcat6" do + action :start +end diff --git a/tomcat6/templates/default/manager.xml.erb b/tomcat6/templates/default/manager.xml.erb new file mode 100644 index 0000000..34551e9 --- /dev/null +++ b/tomcat6/templates/default/manager.xml.erb @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/tomcat6/templates/default/tomcat-users.xml.erb b/tomcat6/templates/default/tomcat-users.xml.erb new file mode 100644 index 0000000..d81c583 --- /dev/null +++ b/tomcat6/templates/default/tomcat-users.xml.erb @@ -0,0 +1,5 @@ + + + + + diff --git a/tomcat6/templates/default/tomcat6.conf.erb b/tomcat6/templates/default/tomcat6.conf.erb new file mode 100644 index 0000000..e1746a2 --- /dev/null +++ b/tomcat6/templates/default/tomcat6.conf.erb @@ -0,0 +1,55 @@ +# System-wide configuration file for tomcat6 services +# This will be sourced by tomcat6 and any secondary service +# Values will be overridden by service-specific configuration +# files in /etc/sysconfig +# +# Use this one to change default values for all services +# Change the service specific ones to affect only one service +# (see, for instance, /etc/sysconfig/tomcat6) +# + +# Where your java installation lives +JAVA_HOME="<%= @node[:tomcat6][:java_home]%>" + +# Where your tomcat installation lives +CATALINA_BASE="<%= @node[:tomcat6][:home]%>" +CATALINA_HOME="<%= @node[:tomcat6][:home]%>" +JASPER_HOME="<%= @node[:tomcat6][:home]%>" +CATALINA_TMPDIR="<%= @node[:tomcat6][:temp]%>" + +# You can pass some parameters to java here if you wish to +#JAVA_OPTS="-Xminf0.1 -Xmaxf0.3" +JAVA_OPTS="-Dcom.sun.management.snmp.interface=0.0.0.0 -Dcom.sun.management.snmp.acl=false -Dcom.sun.management.snmp.port=1161 <%= @node[:tomcat6][:java_opts]%> " +# Use JAVA_OPTS to set java.library.path for libtcnative.so +#JAVA_OPTS="-Djava.library.path=/usr/lib" +#JAVA_OPTS="-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n" +# What user should run tomcat +TOMCAT_USER="<%= @node[:tomcat6][:user]%>" + +# You can change your tomcat locale here +#LANG="en_US" + +# set the timezone + +TZ='Universal' +export TZ +# Run tomcat under the Java Security Manager +SECURITY_MANAGER="false" + +# Time to wait in seconds, before killing process +SHUTDOWN_WAIT="3" + +# Whether to annoy the user with "attempting to shut down" messages or not +SHUTDOWN_VERBOSE="false" + +# Set the TOMCAT_PID location +CATALINA_PID="/var/run/tomcat6.pid" + +# Connector port is 8080 for this tomcat6 instance +#CONNECTOR_PORT="8080" + +# If you wish to further customize your tomcat environment, +# put your own definitions here +# (i.e. LD_LIBRARY_PATH for some jdbc drivers) + +LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/share/tomcat6/jai-1_1_3/lib/ diff --git a/ubuntu/metadata.json b/ubuntu/metadata.json new file mode 100644 index 0000000..27fe29e --- /dev/null +++ b/ubuntu/metadata.json @@ -0,0 +1,42 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Sets up sources for ubuntu", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "ubuntu": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ] + }, + "version": "0.7.0", + "name": "ubuntu", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "ubuntu": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + "apt": [ + + ] + } +} \ No newline at end of file diff --git a/ubuntu/metadata.rb b/ubuntu/metadata.rb new file mode 100644 index 0000000..02694a7 --- /dev/null +++ b/ubuntu/metadata.rb @@ -0,0 +1,7 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Sets up sources for ubuntu" +version "0.8" +depends "apt" +supports "ubuntu" diff --git a/ubuntu/recipes/default.rb b/ubuntu/recipes/default.rb new file mode 100644 index 0000000..9990d51 --- /dev/null +++ b/ubuntu/recipes/default.rb @@ -0,0 +1,27 @@ +# +# Cookbook Name:: ubuntu +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apt" + +template "/etc/apt/sources.list" do + mode 0644 + variables :code_name => node[:lsb][:codename] + notifies :run, resources(:execute => "apt-get update"), :immediately + source "sources.list.erb" +end diff --git a/ubuntu/templates/default/sources.list.erb b/ubuntu/templates/default/sources.list.erb new file mode 100644 index 0000000..e788d2e --- /dev/null +++ b/ubuntu/templates/default/sources.list.erb @@ -0,0 +1,15 @@ +# +# Ubuntu <%= @code_name %> - Generated by Chef +# + +deb http://us.archive.ubuntu.com/ubuntu <%= @code_name %> main restricted universe multiverse +deb-src http://us.archive.ubuntu.com/ubuntu <%= @code_name %> main restricted universe multiverse + +deb http://us.archive.ubuntu.com/ubuntu <%= @code_name %>-updates main restricted universe multiverse +deb-src http://us.archive.ubuntu.com/ubuntu <%= @code_name %>-updates main restricted universe multiverse + +# +# Security updates +# +deb http://security.ubuntu.com/ubuntu <%= @code_name %>-security main restricted universe multiverse +deb-src http://security.ubuntu.com/ubuntu <%= @code_name %>-security main restricted universe multiverse diff --git a/unicorn/README.rdoc b/unicorn/README.rdoc new file mode 100644 index 0000000..54c550b --- /dev/null +++ b/unicorn/README.rdoc @@ -0,0 +1,17 @@ += LICENSE AND AUTHOR: + +Author:: Adam Jacob + +Copyright 2009-2010, Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/unicorn/definitions/unicorn_config.rb b/unicorn/definitions/unicorn_config.rb new file mode 100644 index 0000000..86c5fb0 --- /dev/null +++ b/unicorn/definitions/unicorn_config.rb @@ -0,0 +1,49 @@ +# +# Author:: Adam Jacob +# Cookbook Name:: unicorn +# Definition:: unicorn_config +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :unicorn_config, :listen => nil, :worker_timeout => 60, :preload_app => false, :worker_processes => 4, :before_fork => nil, :after_fork => nil, :pid => nil, :stderr_path => nil, :stdout_path => nil, :notifies => nil, :owner => nil, :group => nil, :mode => nil do + config_dir = File.dirname(params[:name]) + + directory config_dir do + recursive true + action :create + end + + tvars = params.clone + params[:listen].each do |port, options| + oarray = Array.new + options.each do |k, v| + oarray << ":#{k} => #{v}" + end + tvars[:listen][port] = oarray.join(", ") + end + + template params[:name] do + source "unicorn.rb.erb" + cookbook "unicorn" + mode "0644" + owner params[:owner] if params[:owner] + group params[:group] if params[:group] + mode params[:mode] if params[:mode] + variables params + notifies *params[:notifies] if params[:notifies] + end + +end diff --git a/unicorn/metadata.rb b/unicorn/metadata.rb new file mode 100644 index 0000000..aa872d0 --- /dev/null +++ b/unicorn/metadata.rb @@ -0,0 +1,8 @@ +maintainer "Opscode, Inc" +maintainer_email "ops@opscode.com" +license "Apache 2.0" +description "Installs/Configures unicorn" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.1" +depends "ruby" +depends "rubygems" diff --git a/unicorn/recipes/default.rb b/unicorn/recipes/default.rb new file mode 100644 index 0000000..dd71946 --- /dev/null +++ b/unicorn/recipes/default.rb @@ -0,0 +1,24 @@ +# +# Author:: Adam Jacob +# Cookbook Name:: unicorn +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "ruby" +include_recipe "rubygems" + +gem_package "unicorn" diff --git a/unicorn/templates/default/unicorn.rb.erb b/unicorn/templates/default/unicorn.rb.erb new file mode 100644 index 0000000..455bf1d --- /dev/null +++ b/unicorn/templates/default/unicorn.rb.erb @@ -0,0 +1,48 @@ +## +# Unicorn config at <%= @name %> +# Managed by Chef - Local Changes will be Nuked from Orbit (just to be sure) +## + +# What ports/sockets to listen on, and what options for them. +<%- @listen.each do |port, options| %> +listen <%= port %>, <%= options %> +<%- end %> + +# What the timeout for killing busy workers is, in seconds +timeout <%= @worker_timeout %> + +# Whether the app should be pre-loaded +preload_app <%= @preload_app %> + +# How many worker processes +worker_processes <%= @worker_processes %> + +<%- if @before_fork %> +# What to do before we fork a worker +before_fork do |server, worker| + <%= @before_fork %> +end + +<%- end %> +<%- if @after_fork %> +# What to do after we fork a worker +before_fork do |server, worker| + <%= @before_fork %> +end + +<%- end %> +<%- if @pid %> +# Where to drop a pidfile +pid '<%= @pid %>' + +<%- end %> +<%- if @stderr_path %> +# Where stderr gets logged +stderr_path '<%= @stderr_path %>' + +<%- end %> +<%- if @stdout_path %> +# Where stdout gets logged +stdout_path '<%= @stdout_path %>' + +<%- end %> diff --git a/users/attributes/default.rb b/users/attributes/default.rb new file mode 100755 index 0000000..3905695 --- /dev/null +++ b/users/attributes/default.rb @@ -0,0 +1,4 @@ +users Mash.new unless attribute?("users") + +# passwords must be in shadow password format with a salt. To generate: openssl passwd -1 +# users[:jose] = {:password => "shadowpass", :comment => "José Amador", :ssh_key => "..." } diff --git a/users/definitions/add_keys.rb b/users/definitions/add_keys.rb new file mode 100755 index 0000000..431aa49 --- /dev/null +++ b/users/definitions/add_keys.rb @@ -0,0 +1,38 @@ +define :add_keys, :conf => {} do + config = params[:conf] + name = params[:name] + keys = Mash.new + keys[name] = node[:ssh_keys][name] + + if config[:ssh_key_groups] + config[:ssh_key_groups].each do |group| + node[:users].find_all { |u| u.last[:groups].include?(group) }.each do |user| + keys[user.first] = node[:ssh_keys][user.first] + end + end + end + + if config[:extra_ssh_keys] + config[:extra_ssh_keys].each do |username| + keys[username] = node[:ssh_keys][username] + end + end + + directory "/home/#{name}/.ssh" do + action :create + owner name + group config[:groups] ? config[:groups].first.to_s : name + mode 0755 + not_if { File.exists? "/home/#{name}/.ssh" } + end + + template "/home/#{name}/.ssh/authorized_keys" do + source "authorized_keys.erb" + action :create + owner name + group config[:groups] ? config[:groups].first.to_s : name + variables(:keys => keys) + mode 0600 + not_if { defined?(node[:users][name][:preserve_keys]) ? node[:users][name][:preserve_keys] : false } + end +end \ No newline at end of file diff --git a/users/libraries/roles.rb b/users/libraries/roles.rb new file mode 100755 index 0000000..8b98106 --- /dev/null +++ b/users/libraries/roles.rb @@ -0,0 +1,17 @@ +def user_is_in_role?(username) + return false if !@node[:role] + Chef::Log.info role[:groups].inspect + role[:groups].include? get_user(username)[:group] +end + +def role + @node[:roles][@node[:role]] +end + +# method name 'user' conflicts with chef, so we use 'get_user' +def get_user(username) + Chef::Log.info username + user = @node[:users][username] + Chef::Log.info user.inspect + user +end diff --git a/users/metadata.json b/users/metadata.json new file mode 100755 index 0000000..1990bd8 --- /dev/null +++ b/users/metadata.json @@ -0,0 +1,38 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + "users": "" + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures users and groups", + "version": "0.1.0", + "name": "users", + "providing": { + "users": [ + + ] + } +} \ No newline at end of file diff --git a/users/metadata.rb b/users/metadata.rb new file mode 100755 index 0000000..a671e4b --- /dev/null +++ b/users/metadata.rb @@ -0,0 +1,4 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures users and groups" +version "0.1" diff --git a/users/recipes/default.rb b/users/recipes/default.rb new file mode 100755 index 0000000..4499e16 --- /dev/null +++ b/users/recipes/default.rb @@ -0,0 +1,17 @@ +include_recipe "ruby-shadow" + +if node[:users] + node[:users].keys.each do |username| + config = node[:users][username] + user username do + comment config[:comment] + home "/home/#{username}" + shell "/bin/bash" + password config[:password] + supports :manage_home => true + action [:create, :manage] + end + + add_keys username + end +end diff --git a/users/templates/default/authorized_keys.erb b/users/templates/default/authorized_keys.erb new file mode 100755 index 0000000..71491f4 --- /dev/null +++ b/users/templates/default/authorized_keys.erb @@ -0,0 +1,4 @@ +<% @keys.each do |name, key| %> +# <%= name %> +<%= key %> +<% end %> \ No newline at end of file diff --git a/varnish/attributes/varnish.rb b/varnish/attributes/varnish.rb new file mode 100644 index 0000000..1bb9cbd --- /dev/null +++ b/varnish/attributes/varnish.rb @@ -0,0 +1,5 @@ +case platform +when "debian","ubuntu" + set[:varnish][:dir] = "/etc/varnish" + set[:varnish][:default] = "/etc/default/varnish" +end diff --git a/varnish/metadata.json b/varnish/metadata.json new file mode 100644 index 0000000..9755e23 --- /dev/null +++ b/varnish/metadata.json @@ -0,0 +1,43 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Instsalls and configures varnish", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "varnish": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "varnish", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "varnish": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/varnish/metadata.rb b/varnish/metadata.rb new file mode 100644 index 0000000..d7b106f --- /dev/null +++ b/varnish/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Instsalls and configures varnish" +version "0.7" + +%w{ubuntu debian}.each do |os| + supports os +end diff --git a/varnish/recipes/default.rb b/varnish/recipes/default.rb new file mode 100644 index 0000000..8703706 --- /dev/null +++ b/varnish/recipes/default.rb @@ -0,0 +1,44 @@ +# Cookbook Name:: varnish +# Recipe:: default +# Author:: Joe Williams +# +# Copyright 2008-2009, Joe Williams +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "varnish" + +template "#{node[:varnish][:dir]}default.vcl" do + source "default.vcl.erb" + owner "root" + group "root" + mode 0644 +end + +template "#{node[:varnish][:default]}" do + source "ubuntu-default.erb" + owner "root" + group "root" + mode 0644 +end + +service "varnish" do + supports :restart => true, :reload => true + action [ :enable, :start ] +end + +service "varnishlog" do + supports :restart => true, :reload => true + action [ :enable, :start ] +end diff --git a/varnish/templates/default/default.vcl.erb b/varnish/templates/default/default.vcl.erb new file mode 100644 index 0000000..81760e0 --- /dev/null +++ b/varnish/templates/default/default.vcl.erb @@ -0,0 +1,11 @@ +backend default { + .host = "localhost"; + .port = "8080"; +} + +sub vcl_fetch { + # force minimum ttl of 120 seconds + if (obj.ttl < 120s) { + set obj.ttl = 120s; + } +} diff --git a/varnish/templates/default/ubuntu-default.erb b/varnish/templates/default/ubuntu-default.erb new file mode 100644 index 0000000..88d35f2 --- /dev/null +++ b/varnish/templates/default/ubuntu-default.erb @@ -0,0 +1,92 @@ +# Configuration file for varnish +# +# /etc/init.d/varnish expects the variable $DAEMON_OPTS to be set from this +# shell script fragment. +# + +# Maximum number of open files (for ulimit -n) +NFILES=131072 + +# Default varnish instance name is the local nodename. Can be overridden with +# the -n switch, to have more instances on a single server. +INSTANCE=$(uname -n) + +# This file contains 4 alternatives, please use only one. + +## Alternative 1, Minimal configuration, no VCL +# +# Listen on port 6081, administration on localhost:6082, and forward to +# content server on localhost:8080. Use a fixed-size cache file. +# +DAEMON_OPTS="-a :6081 \ + -T localhost:6082 \ + -b localhost:8080 \ + -u varnish -g varnish \ + -s file,/var/lib/varnish/$INSTANCE/varnish_storage.bin,1G" + + +## Alternative 2, Configuration with VCL +# +# Listen on port 6081, administration on localhost:6082, and forward to +# one content server selected by the vcl file, based on the request. Use a +# fixed-size cache file. +# +# DAEMON_OPTS="-a :6081 \ +# -T localhost:6082 \ +# -f /etc/varnish/default.vcl \ +# -s file,/var/lib/varnish/$INSTANCE/varnish_storage.bin,1G" + + +## Alternative 3, Advanced configuration +# +# See varnishd(1) for more information. +# +# # Main configuration file. You probably want to change it :) +# VARNISH_VCL_CONF=/etc/varnish/default.vcl +# +# # Default address and port to bind to +# # Blank address means all IPv4 and IPv6 interfaces, otherwise specify +# # a host name, an IPv4 dotted quad, or an IPv6 address in brackets. +# VARNISH_LISTEN_ADDRESS= +# VARNISH_LISTEN_PORT=6081 +# +# # Telnet admin interface listen address and port +# VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 +# VARNISH_ADMIN_LISTEN_PORT=6082 +# +# # The minimum number of worker threads to start +# VARNISH_MIN_THREADS=1 +# +# # The Maximum number of worker threads to start +# VARNISH_MAX_THREADS=1000 +# +# # Idle timeout for worker threads +# VARNISH_THREAD_TIMEOUT=120 +# +# # Cache file location +# VARNISH_STORAGE_FILE=/var/lib/varnish/$INSTANCE/varnish_storage.bin +# +# # Cache file size: in bytes, optionally using k / M / G / T suffix, +# # or in percentage of available disk space using the % suffix. +# VARNISH_STORAGE_SIZE=1G +# +# # Backend storage specification +# VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}" +# +# # Default TTL used when the backend does not specify one +# VARNISH_TTL=120 +# +# # DAEMON_OPTS is used by the init script. If you add or remove options, make +# # sure you update this section, too. +# DAEMON_OPTS="-a ${VARNISH_LISTEN_ADDRESS}:${VARNISH_LISTEN_PORT} \ +# -f ${VARNISH_VCL_CONF} \ +# -T ${VARNISH_ADMIN_LISTEN_ADDRESS}:${VARNISH_ADMIN_LISTEN_PORT} \ +# -t ${VARNISH_TTL} \ +# -w ${VARNISH_MIN_THREADS},${VARNISH_MAX_THREADS},${VARNISH_THREAD_TIMEOUT} \ +# -s ${VARNISH_STORAGE}" +# + + +## Alternative 4, Do It Yourself +# +# DAEMON_OPTS="" \ No newline at end of file diff --git a/xen/create_slice/example b/xen/create_slice/example new file mode 100755 index 0000000..ca0dd1e --- /dev/null +++ b/xen/create_slice/example @@ -0,0 +1,24 @@ +Copy the stock image to a new volume on a dom0. +Mount the new volume, and drop in working /etc/network/interfaces, /etc/hostname, and /root/.ssh/authorized_keys. +Generate a Xen config file in /etc/xen/auto. +xm create ... +cool. the copy process is a dd? +Actually, restore. +might as well get a recipe going for it +So, it's really: +/usr/sbin/lvcreate -L 16G -n slice_root VolGroupXen +/usr/sbin/lvcreate -L 2G -n slice_swap +^ VolGroupXen +where can i try this? +Any of the xens. +We need to copy the stock image around, it's currently in my NFS homedir. +/sbin/mkfs.ext3 /dev/mapper/VolGroupXen-slice_root +/sbin/mkswap /dev/mapper/VolGroupXen-slice_swap +mkdir /tmp/mnt.slice_root +mount -t ext3 /dev/mapper/VolGroupXen-slice_root /tmp/mnt.slice_root +cd /tmp/mnt.slice_root +restore -rf /home/mark/intrepid.dump +cd / +umount /tmp/mnt.slice_root +cool +"slice" is replaced with the name of the slice everywhere, obviously. diff --git a/xen/create_slice/libraries/default.rb b/xen/create_slice/libraries/default.rb new file mode 100755 index 0000000..d808456 --- /dev/null +++ b/xen/create_slice/libraries/default.rb @@ -0,0 +1,3 @@ +def generate_mac_address + "00:16:3E:%X%X:%X%X:%X%X" % Array.new(6) { rand(16) } +end \ No newline at end of file diff --git a/xen/create_slice/templates/default/slice_config.xen.erb b/xen/create_slice/templates/default/slice_config.xen.erb new file mode 100755 index 0000000..07165a0 --- /dev/null +++ b/xen/create_slice/templates/default/slice_config.xen.erb @@ -0,0 +1,10 @@ +kernel = "/boot/vmlinuz-2.6.18-92.1.6.el5xen" +ramdisk = "/boot/initrd-2.6.18-92.1.6.el5xen.img" +extra = "xencons=tty maxmem=<%= @maxmem %>M" +name = "<%= @name %>" +disk = [ 'phy:mapper/VolGroupXen-<%= @name %>_root,xvda,w', 'phy:mapper/VolGroupXen-<%= @name %>_swap,xvdb,w' ] +vif = [ 'mac=<%= @mac_address %>, bridge=br0' ] +bootloader="/usr/bin/pygrub" +vcpus=<%= @vcpus %> +memory="<%= @memory %>" +maxmem="<%= @maxmem %>" diff --git a/xen/metadata.json b/xen/metadata.json new file mode 100755 index 0000000..29afbfa --- /dev/null +++ b/xen/metadata.json @@ -0,0 +1,36 @@ +{ + "replacing": { + + }, + "long_description": "", + "attributes": { + + }, + "maintainer": "37signals", + "recommendations": { + + }, + "license": "Apache v2.0", + "recipes": { + + }, + "maintainer_email": "sysadmins@37signals.com", + "suggestions": { + + }, + "dependencies": { + + }, + "conflicting": { + + }, + "platforms": { + + }, + "description": "Configures xen", + "version": "0.1.0", + "name": "xen", + "providing": { + + } +} \ No newline at end of file diff --git a/xen/metadata.rb b/xen/metadata.rb new file mode 100755 index 0000000..ae509af --- /dev/null +++ b/xen/metadata.rb @@ -0,0 +1,4 @@ +maintainer "37signals" +maintainer_email "sysadmins@37signals.com" +description "Configures xen" +version "0.1" diff --git a/xfs/README.rdoc b/xfs/README.rdoc new file mode 100644 index 0000000..4246de2 --- /dev/null +++ b/xfs/README.rdoc @@ -0,0 +1,24 @@ += DESCRIPTION: + +Installs packages for working with XFS filesystems. + += REQUIREMENTS: + +Ubuntu or Debian package names are assumed. + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/xfs/metadata.json b/xfs/metadata.json new file mode 100644 index 0000000..c0c6fec --- /dev/null +++ b/xfs/metadata.json @@ -0,0 +1,46 @@ +{ + "replacing": { + + }, + "dependencies": { + + }, + "groupings": { + + }, + "long_description": "= DESCRIPTION:\n\nInstalls packages for working with XFS filesystems.\n\n= REQUIREMENTS:\n\nUbuntu or Debian package names are assumed.\n\n= LICENSE and AUTHOR:\n\nAuthor:: Joshua Timberman ()\nCopyright:: 2009, Opscode, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "description": "Installs packages for working with XFS", + "recommendations": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.1.0", + "maintainer": "Opscode, Inc.", + "name": "xfs", + "recipes": { + "xfs": "" + }, + "suggestions": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "xfs": [ + + ] + }, + "license": "Apache 2.0" +} \ No newline at end of file diff --git a/xfs/metadata.rb b/xfs/metadata.rb new file mode 100644 index 0000000..3d8ff75 --- /dev/null +++ b/xfs/metadata.rb @@ -0,0 +1,10 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs packages for working with XFS" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.1" + +%w{ debian ubuntu }.each do |os| + supports os +end diff --git a/xfs/recipes/default.rb b/xfs/recipes/default.rb new file mode 100644 index 0000000..f906bbf --- /dev/null +++ b/xfs/recipes/default.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: xfs +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +%w{ xfsprogs xfsdump xfslibs-dev }.each do |pkg| + package pkg +end diff --git a/zsh/metadata.json b/zsh/metadata.json new file mode 100644 index 0000000..2900f88 --- /dev/null +++ b/zsh/metadata.json @@ -0,0 +1,43 @@ +{ + "maintainer": "Opscode, Inc.", + "description": "Installs zsh", + "recommendations": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "recipes": { + "zsh": "" + }, + "suggestions": { + + }, + "platforms": { + "ubuntu": [ + + ], + "debian": [ + + ] + }, + "version": "0.7.0", + "name": "zsh", + "conflicting": { + + }, + "attributes": { + + }, + "providing": { + "zsh": [ + + ] + }, + "license": "Apache 2.0", + "long_description": "", + "replacing": { + + }, + "dependencies": { + + } +} \ No newline at end of file diff --git a/zsh/metadata.rb b/zsh/metadata.rb new file mode 100644 index 0000000..6d78f6f --- /dev/null +++ b/zsh/metadata.rb @@ -0,0 +1,9 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs zsh" +version "0.7" + +%w{ubuntu debian}.each do |os| + supports os +end diff --git a/zsh/recipes/default.rb b/zsh/recipes/default.rb new file mode 100644 index 0000000..3f07689 --- /dev/null +++ b/zsh/recipes/default.rb @@ -0,0 +1,29 @@ +# +# Cookbook Name:: zsh +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "zsh" do + action :install +end + +case node[:platform] +when "ubuntu" + package "zsh-doc" do + action :install + end +end

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