Repository: auser/poolparty
Branch: master
Commit: 8b4af051833a
Files: 341
Total size: 659.8 KB
Directory structure:
gitextract_s60rxf8b/
├── .gitignore
├── .gitmodules
├── .rdebugrc
├── .rvmrc
├── Gemfile
├── License.txt
├── Makefile
├── Manifest.txt
├── README.rdoc
├── Rakefile
├── Vagrantfile
├── bin/
│ ├── cloud
│ ├── cloud-bootstrap
│ ├── cloud-compile
│ ├── cloud-configure
│ ├── cloud-console
│ ├── cloud-contract
│ ├── cloud-cssh
│ ├── cloud-expand
│ ├── cloud-list
│ ├── cloud-reboot
│ ├── cloud-rsync
│ ├── cloud-run
│ ├── cloud-show
│ ├── cloud-ssh
│ ├── cloud-start
│ ├── cloud-terminate
│ └── cloud-vnc
├── config/
│ └── requirements.rb
├── examples/
│ ├── README.md
│ ├── chef_cloud/
│ │ ├── chef_repo/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ ├── Rakefile
│ │ │ ├── certificates/
│ │ │ │ └── README
│ │ │ ├── config/
│ │ │ │ ├── client.rb.example
│ │ │ │ ├── rake.rb
│ │ │ │ ├── server.rb.example
│ │ │ │ └── solo.rb.example
│ │ │ ├── cookbooks/
│ │ │ │ ├── README
│ │ │ │ ├── apache2/
│ │ │ │ │ ├── README.rdoc
│ │ │ │ │ ├── attributes/
│ │ │ │ │ │ └── apache.rb
│ │ │ │ │ ├── definitions/
│ │ │ │ │ │ ├── apache_conf.rb
│ │ │ │ │ │ ├── apache_module.rb
│ │ │ │ │ │ ├── apache_site.rb
│ │ │ │ │ │ └── web_app.rb
│ │ │ │ │ ├── files/
│ │ │ │ │ │ └── default/
│ │ │ │ │ │ └── apache2_module_conf_generate.pl
│ │ │ │ │ ├── metadata.json
│ │ │ │ │ ├── metadata.rb
│ │ │ │ │ ├── recipes/
│ │ │ │ │ │ ├── default.rb
│ │ │ │ │ │ ├── god_monitor.rb
│ │ │ │ │ │ ├── mod_alias.rb
│ │ │ │ │ │ ├── mod_auth_basic.rb
│ │ │ │ │ │ ├── mod_auth_digest.rb
│ │ │ │ │ │ ├── mod_authn_file.rb
│ │ │ │ │ │ ├── mod_authnz_ldap.rb
│ │ │ │ │ │ ├── mod_authz_default.rb
│ │ │ │ │ │ ├── mod_authz_groupfile.rb
│ │ │ │ │ │ ├── mod_authz_host.rb
│ │ │ │ │ │ ├── mod_authz_user.rb
│ │ │ │ │ │ ├── mod_autoindex.rb
│ │ │ │ │ │ ├── mod_cgi.rb
│ │ │ │ │ │ ├── mod_dav.rb
│ │ │ │ │ │ ├── mod_dav_svn.rb
│ │ │ │ │ │ ├── mod_deflate.rb
│ │ │ │ │ │ ├── mod_dir.rb
│ │ │ │ │ │ ├── mod_env.rb
│ │ │ │ │ │ ├── mod_expires.rb
│ │ │ │ │ │ ├── mod_fcgid.rb
│ │ │ │ │ │ ├── mod_headers.rb
│ │ │ │ │ │ ├── mod_ldap.rb
│ │ │ │ │ │ ├── mod_log_config.rb
│ │ │ │ │ │ ├── mod_mime.rb
│ │ │ │ │ │ ├── mod_negotiation.rb
│ │ │ │ │ │ ├── mod_php5.rb
│ │ │ │ │ │ ├── mod_proxy.rb
│ │ │ │ │ │ ├── mod_proxy_ajp.rb
│ │ │ │ │ │ ├── mod_proxy_balancer.rb
│ │ │ │ │ │ ├── mod_proxy_connect.rb
│ │ │ │ │ │ ├── mod_proxy_http.rb
│ │ │ │ │ │ ├── mod_python.rb
│ │ │ │ │ │ ├── mod_rails.rb
│ │ │ │ │ │ ├── mod_rewrite.rb
│ │ │ │ │ │ ├── mod_setenvif.rb
│ │ │ │ │ │ ├── mod_ssl.rb
│ │ │ │ │ │ └── mod_status.rb
│ │ │ │ │ └── templates/
│ │ │ │ │ └── default/
│ │ │ │ │ ├── a2dismod.erb
│ │ │ │ │ ├── a2dissite.erb
│ │ │ │ │ ├── a2enmod.erb
│ │ │ │ │ ├── a2ensite.erb
│ │ │ │ │ ├── apache2.conf.erb
│ │ │ │ │ ├── apache2.god.erb
│ │ │ │ │ ├── charset.erb
│ │ │ │ │ ├── default-site.erb
│ │ │ │ │ ├── mods/
│ │ │ │ │ │ ├── README
│ │ │ │ │ │ ├── alias.conf.erb
│ │ │ │ │ │ ├── autoindex.conf.erb
│ │ │ │ │ │ ├── deflate.conf.erb
│ │ │ │ │ │ ├── dir.conf.erb
│ │ │ │ │ │ ├── fcgid.conf.erb
│ │ │ │ │ │ ├── mime.conf.erb
│ │ │ │ │ │ ├── negotiation.conf.erb
│ │ │ │ │ │ ├── proxy.conf.erb
│ │ │ │ │ │ ├── setenvif.conf.erb
│ │ │ │ │ │ ├── ssl.conf.erb
│ │ │ │ │ │ └── status.conf.erb
│ │ │ │ │ ├── port_apache.erb
│ │ │ │ │ ├── ports.conf.erb
│ │ │ │ │ ├── security.erb
│ │ │ │ │ └── web_app.conf.erb
│ │ │ │ ├── apt/
│ │ │ │ │ ├── files/
│ │ │ │ │ │ └── default/
│ │ │ │ │ │ ├── apt-cacher
│ │ │ │ │ │ ├── apt-cacher.conf
│ │ │ │ │ │ └── apt-proxy-v2.conf
│ │ │ │ │ ├── metadata.json
│ │ │ │ │ ├── metadata.rb
│ │ │ │ │ └── recipes/
│ │ │ │ │ ├── cacher.rb
│ │ │ │ │ ├── default.rb
│ │ │ │ │ └── proxy.rb
│ │ │ │ ├── bootstrap/
│ │ │ │ │ ├── README.rdoc
│ │ │ │ │ ├── attributes/
│ │ │ │ │ │ └── bootstrap.rb
│ │ │ │ │ ├── metadata.json
│ │ │ │ │ ├── metadata.rb
│ │ │ │ │ ├── recipes/
│ │ │ │ │ │ ├── client.rb
│ │ │ │ │ │ ├── default.rb
│ │ │ │ │ │ └── server.rb
│ │ │ │ │ └── templates/
│ │ │ │ │ └── default/
│ │ │ │ │ ├── client.rb.erb
│ │ │ │ │ ├── server.rb.erb
│ │ │ │ │ ├── sv-chef-client-log-run.erb
│ │ │ │ │ ├── sv-chef-client-run.erb
│ │ │ │ │ ├── sv-chef-indexer-log-run.erb
│ │ │ │ │ ├── sv-chef-indexer-run.erb
│ │ │ │ │ ├── sv-chef-server-log-run.erb
│ │ │ │ │ └── sv-chef-server-run.erb
│ │ │ │ ├── chef/
│ │ │ │ │ ├── README.rdoc
│ │ │ │ │ ├── attributes/
│ │ │ │ │ │ └── chef.rb
│ │ │ │ │ ├── metadata.json
│ │ │ │ │ ├── metadata.rb
│ │ │ │ │ ├── recipes/
│ │ │ │ │ │ ├── client.rb
│ │ │ │ │ │ ├── default.rb
│ │ │ │ │ │ ├── server.rb
│ │ │ │ │ │ └── server_proxy.rb
│ │ │ │ │ └── templates/
│ │ │ │ │ └── default/
│ │ │ │ │ ├── chef_server.conf.erb
│ │ │ │ │ ├── client.rb.erb
│ │ │ │ │ └── server.rb.erb
│ │ │ │ ├── ec2/
│ │ │ │ │ ├── attributes/
│ │ │ │ │ │ ├── ec2_metadata.rb
│ │ │ │ │ │ └── ec2_recipe_options.rb
│ │ │ │ │ ├── metadata.rb
│ │ │ │ │ └── recipes/
│ │ │ │ │ └── default.rb
│ │ │ │ ├── ganglia/
│ │ │ │ │ ├── README.rdoc
│ │ │ │ │ ├── attributes/
│ │ │ │ │ │ └── default.rb
│ │ │ │ │ ├── metadata.rb
│ │ │ │ │ ├── recipes/
│ │ │ │ │ │ ├── default.rb
│ │ │ │ │ │ ├── gmetad.rb
│ │ │ │ │ │ ├── monitor_sshd.rb
│ │ │ │ │ │ ├── monitor_watson.rb
│ │ │ │ │ │ └── web.rb
│ │ │ │ │ └── templates/
│ │ │ │ │ └── default/
│ │ │ │ │ ├── bin/
│ │ │ │ │ │ ├── gmetad.erb
│ │ │ │ │ │ ├── gmond.erb
│ │ │ │ │ │ └── monitors/
│ │ │ │ │ │ ├── sshd_ganglia.sh.erb
│ │ │ │ │ │ └── watson_channels.sh.erb
│ │ │ │ │ ├── ganglia-web-conf.php.erb
│ │ │ │ │ ├── gmetad.conf.erb
│ │ │ │ │ ├── gmond.conf.erb
│ │ │ │ │ └── jaunty.sources.list.erb
│ │ │ │ ├── ntp/
│ │ │ │ │ ├── attributes/
│ │ │ │ │ │ └── ntp.rb
│ │ │ │ │ ├── metadata.json
│ │ │ │ │ ├── metadata.rb
│ │ │ │ │ ├── recipes/
│ │ │ │ │ │ └── default.rb
│ │ │ │ │ └── templates/
│ │ │ │ │ └── default/
│ │ │ │ │ └── ntp.conf.erb
│ │ │ │ ├── rsyslog/
│ │ │ │ │ ├── README.rdoc
│ │ │ │ │ ├── attributes/
│ │ │ │ │ │ └── rsyslog.rb
│ │ │ │ │ ├── files/
│ │ │ │ │ │ ├── default/
│ │ │ │ │ │ │ └── rsyslog.default
│ │ │ │ │ │ ├── ubuntu-8.04/
│ │ │ │ │ │ │ └── rsyslog.default
│ │ │ │ │ │ └── ubuntu-9.10/
│ │ │ │ │ │ └── rsyslog.default
│ │ │ │ │ ├── metadata.json
│ │ │ │ │ ├── metadata.rb
│ │ │ │ │ ├── recipes/
│ │ │ │ │ │ ├── client.rb
│ │ │ │ │ │ ├── default.rb
│ │ │ │ │ │ └── server.rb
│ │ │ │ │ └── templates/
│ │ │ │ │ ├── default/
│ │ │ │ │ │ ├── remote.conf.erb
│ │ │ │ │ │ ├── rsyslog.conf.erb
│ │ │ │ │ │ └── server.conf.erb
│ │ │ │ │ └── ubuntu-9.10/
│ │ │ │ │ ├── 50-default.conf.erb
│ │ │ │ │ ├── remote.conf.erb
│ │ │ │ │ └── server.conf.erb
│ │ │ │ └── runit/
│ │ │ │ ├── attributes/
│ │ │ │ │ └── sv_bin.rb
│ │ │ │ ├── definitions/
│ │ │ │ │ └── runit_service.rb
│ │ │ │ ├── files/
│ │ │ │ │ ├── ubuntu-6.10/
│ │ │ │ │ │ └── runsvdir
│ │ │ │ │ ├── ubuntu-7.04/
│ │ │ │ │ │ └── runsvdir
│ │ │ │ │ ├── ubuntu-7.10/
│ │ │ │ │ │ └── runsvdir
│ │ │ │ │ └── ubuntu-8.04/
│ │ │ │ │ └── runsvdir
│ │ │ │ ├── metadata.json
│ │ │ │ ├── metadata.rb
│ │ │ │ └── recipes/
│ │ │ │ └── default.rb
│ │ │ ├── roles/
│ │ │ │ ├── README
│ │ │ │ └── chef-upgrade.rb
│ │ │ └── site-cookbooks/
│ │ │ ├── README
│ │ │ ├── hosts/
│ │ │ │ ├── attributes/
│ │ │ │ │ └── hosts.rb
│ │ │ │ ├── metadata.rb
│ │ │ │ ├── recipes/
│ │ │ │ │ └── default.rb
│ │ │ │ └── templates/
│ │ │ │ └── default/
│ │ │ │ └── hosts.erb
│ │ │ ├── sysadmin/
│ │ │ │ └── recipes/
│ │ │ │ └── default.rb
│ │ │ └── ubuntu/
│ │ │ ├── metadata.json
│ │ │ ├── metadata.rb
│ │ │ ├── recipes/
│ │ │ │ └── default.rb
│ │ │ └── templates/
│ │ │ └── default/
│ │ │ └── sources.list.erb
│ │ └── user_data
│ ├── chef_cloud.rb
│ ├── ec2_infrastructure_only.rb
│ ├── knock.sh
│ ├── rds_cloud.rb
│ └── simple.rb
├── lib/
│ ├── cloud_providers/
│ │ ├── cloud_provider.rb
│ │ ├── connections.rb
│ │ ├── default/
│ │ │ ├── base.rb
│ │ │ └── helpers/
│ │ │ └── base_helper.rb
│ │ ├── ec2/
│ │ │ ├── ec2.rb
│ │ │ ├── ec2_instance.rb
│ │ │ └── helpers/
│ │ │ ├── authorize.rb
│ │ │ ├── ec2_helper.rb
│ │ │ ├── elastic_auto_scaler.rb
│ │ │ ├── elastic_block_device_mapping.rb
│ │ │ ├── elastic_block_store.rb
│ │ │ ├── elastic_block_store_group.rb
│ │ │ ├── elastic_ip.rb
│ │ │ ├── elastic_load_balancer.rb
│ │ │ ├── rds_instance.rb
│ │ │ ├── revoke.rb
│ │ │ └── security_group.rb
│ │ └── remote_instance.rb
│ ├── cloud_providers.rb
│ ├── core/
│ │ ├── array.rb
│ │ ├── hash.rb
│ │ ├── object.rb
│ │ ├── string.rb
│ │ └── symbol.rb
│ ├── keypair.rb
│ ├── poolparty/
│ │ ├── base.rb
│ │ ├── chef.rb
│ │ ├── chef_attribute.rb
│ │ ├── chef_client.rb
│ │ ├── chef_solo.rb
│ │ ├── cloud.rb
│ │ ├── pool.rb
│ │ ├── pool_party_error.rb
│ │ └── version.rb
│ └── poolparty.rb
├── poolparty.gemspec
├── setup.rb
├── test/
│ ├── fixtures/
│ │ ├── bad_perms_test_key
│ │ ├── chef/
│ │ │ └── recipes/
│ │ │ └── sudo/
│ │ │ ├── attributes/
│ │ │ │ └── sudoers.rb
│ │ │ ├── recipes/
│ │ │ │ └── default.rb
│ │ │ └── templates/
│ │ │ └── default/
│ │ │ └── sudoers.erb
│ │ ├── clouds/
│ │ │ ├── rds_cloud.rb
│ │ │ ├── rds_missing_params.rb
│ │ │ ├── simple_cloud.rb
│ │ │ ├── ssh_cloud.rb
│ │ │ └── vmware_cloud.rb
│ │ ├── ec2/
│ │ │ ├── ec2-describe-instances_response_body.xml
│ │ │ ├── ec2-describe-security-groups_response_body.xml
│ │ │ ├── ec2-run-instances_response_body.xml
│ │ │ ├── ec2-terminate-instances_response_body.xml
│ │ │ ├── elb-describe-load-balancers.xml
│ │ │ └── rds-describe-db-instances-empty_response_body.xml
│ │ ├── keys/
│ │ │ ├── pem_key.pem
│ │ │ ├── pem_pub_key.pem
│ │ │ ├── test_key
│ │ │ └── test_pub_key
│ │ ├── resources/
│ │ │ ├── fake_plugin.rb
│ │ │ ├── fake_resource.rb
│ │ │ ├── fake_subclassed_plugin.rb
│ │ │ └── random_proc_file.rb
│ │ ├── templates/
│ │ │ └── apache_conf.erb
│ │ └── test_template.erb
│ ├── lib/
│ │ ├── core/
│ │ │ ├── array_test.rb
│ │ │ ├── hash_test.rb
│ │ │ ├── object_test.rb
│ │ │ ├── string_test.rb
│ │ │ └── symbol_test.rb
│ │ └── poolparty/
│ │ ├── cloud_test.rb
│ │ ├── keypair_test.rb
│ │ ├── pool_party_error_test.rb
│ │ ├── pool_test.rb
│ │ └── rds_test.rb
│ ├── test_helper.rb
│ └── test_methods.rb
└── vendor/
└── gems/
├── dslify/
│ ├── .gitignore
│ ├── LICENSE
│ ├── README.rdoc
│ ├── Rakefile
│ ├── VERSION.yml
│ ├── dslify.gemspec
│ ├── lib/
│ │ └── dslify.rb
│ └── test/
│ ├── dslify_test.rb
│ └── test_helper.rb
├── git-style-binaries/
│ ├── .document
│ ├── .gitignore
│ ├── .gitmodules
│ ├── README.markdown
│ ├── Rakefile
│ ├── VERSION.yml
│ ├── doc/
│ │ ├── EXAMPLES
│ │ └── poolparty-binaries.screenplay
│ ├── git-style-binaries.gemspec
│ ├── lib/
│ │ ├── ext/
│ │ │ ├── colorize.rb
│ │ │ └── core.rb
│ │ ├── git-style-binary/
│ │ │ ├── autorunner.rb
│ │ │ ├── command.rb
│ │ │ ├── commands/
│ │ │ │ └── help.rb
│ │ │ ├── helpers/
│ │ │ │ ├── name_resolver.rb
│ │ │ │ └── pager.rb
│ │ │ └── parser.rb
│ │ └── git-style-binary.rb
│ └── test/
│ ├── fixtures/
│ │ ├── flickr
│ │ ├── flickr-download
│ │ ├── wordpress
│ │ ├── wordpress-categories
│ │ ├── wordpress-list
│ │ └── wordpress-post
│ ├── git-style-binary/
│ │ └── command_test.rb
│ ├── git_style_binary_test.rb
│ ├── running_binaries_test.rb
│ ├── shoulda_macros/
│ │ └── matching_stdio.rb
│ └── test_helper.rb
├── searchable_paths/
│ ├── .document
│ ├── .gitignore
│ ├── LICENSE
│ ├── README.rdoc
│ ├── Rakefile
│ ├── lib/
│ │ └── searchable_paths.rb
│ └── test/
│ ├── searchable_paths_test.rb
│ └── test_helper.rb
├── trollop/
│ ├── FAQ.txt
│ ├── History.txt
│ ├── Manifest.txt
│ ├── README.txt
│ ├── Rakefile
│ ├── lib/
│ │ └── trollop.rb
│ ├── release-script.txt
│ ├── test/
│ │ └── test_trollop.rb
│ └── www/
│ └── index.html
└── xml-simple/
└── lib/
└── xmlsimple.rb
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
examples-private/
.vagrant
vendor/cache
/trash/
rdoc/*
.com.apple.timemachine.supported
.DS_Store
*tmp*
pkg/*.tgz
pkg/*.gem
pkg/poolparty*/*
.git/
log/*
test_manifest.pp
**/*/erl_crash.dump
log
*.beam
ri/
.autosession.vim
tags
.session
clouds.rb
test/test_dir/
examples/thrift/erlang/deps/thrift/ebin/*.beam
examples/thrift/erlang/ebin/*.beam
*.swp
*~
\#*\#
================================================
FILE: .gitmodules
================================================
[submodule "vendor/gems/amazon-ec2"]
path = vendor/gems/amazon-ec2
url = git://github.com/grempe/amazon-ec2.git
================================================
FILE: .rdebugrc
================================================
set autoeval on
================================================
FILE: .rvmrc
================================================
rvm use ruby-1.9.2@poolparty --create
================================================
FILE: Gemfile
================================================
source "http://rubygems.org"
gemspec
gem 'amazon-ec2', '~>0.9.17'
gem 'xml-simple'
gem 'json'
gem 'git-style-binaries'
group :test do
gem 'fakeweb'
gem 'webmock'
gem 'shoulda'
gem 'rcov'
gem 'mocha'
gem 'right_http_connection'
gem "jnunemaker-matchy", "~> 0.4.0"
gem 'vagrant'
end
group :development do
gem "rdoc"
end
================================================
FILE: License.txt
================================================
Copyright (c) 2009 Ari Lerner
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.
================================================
FILE: Makefile
================================================
RUBY = `which ruby`
RUBYGEMS = `which gem`
GEMENV = `gem env`
all: run_tests
check_install:
$(RUBY) -v
$(RUBYGEMS) -v
show_env:
echo $(GEMENV)
test: run_tests
run_tests:
rake test
================================================
FILE: Manifest.txt
================================================
================================================
FILE: README.rdoc
================================================
= PoolParty
http://poolpartyrb.com
== DESCRIPTION:
PoolParty makes cloud provisioning and management easy. PoolParty provides a unified interface for defining and managing cloud infrasturcure on different cloud providers (just Ec2 for now).
Code your cloud!
== SYNOPSIS:
PoolParty is written with the intention of being as application-agnostic as possible. It installs only the basic
required software to glue the cloud together on the instances as listed below.
== Quickstart
For instance, to start a basic cloud, let's write one:
pool "demo" do
cloud "app" do
instances 2..10
using :ec2
autoscale do
trigger :lower_threshold => 0.3, :upper_threshold => 1.0, :measure => :cpu
end
security_group do
authorize :from_port => 22, :to_port => 22
end
load_balancer do
listener :external_port => 8080, :internal_port => 8080, :protocol => 'tcp'
end
end
end
Simply by issuing the command:
cloud start
This will create an ec2 auto scaling group that will launch the minimum_instances (2 in this case,) inside a security group. The default naming of most objects, such as security_groups, auto scaling groups, launch_configurations, load_balancers and keypairs follow the poolname-cloudname convention.
There are a number of commands PoolParty offers to interact with your cloud. They include:
* cloud start
* cloud terminate
* cloud reboot
* cloud configure
* cloud compile
* cloud console
* cloud expand
* cloud contract
* cloud list
* cloud show
* cloud ssh
* cloud run
Clouds are distinguished by security groups. If a security group is not specified in your cloud block, one will be created based on the naming convention poolname-cloudname.
== Extending
To add a cloud_provider, there are four methods that need to be implemented. Simply sublcass the CloudProviders module and require it in your clouds.rb (or commit it back to PoolParty). Those four methods are:
* run_instance
* terminate_instance
* describe_instances
* describe_instance
== Examples
Checkout the [examples](examples/) directory and its README for some more ideas and examples.
That's it!
More documentation can be found at the site: http://poolpartyrb.com.
== REQUIREMENTS:
* Ruby
== INSTALL:
from gemcutter.org;
sudo gem install poolparty
== LICENSE:
(The MIT License)
Copyright (c) 2009 Ari Lerner, CloudTeam
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.
================================================
FILE: Rakefile
================================================
$:.unshift(File.join(File.dirname(__FILE__), "."))
require 'rake'
require "bundler/gem_tasks"
require 'rake/testtask'
require 'rdoc/task'
require 'config/requirements'
task :default => [:test, :cleanup_test]
desc "Update vendor directory and run tests"
namespace :poolparty do
namespace :vendor do
desc "Fetch all the submodules"
task :submodules do
`git submodule update`
end
end
end
task :vendor => ["poolparty:vendor:submodules"]
task :cleanup_test do
::FileUtils.rm_rf "/tmp/poolparty"
end
# task :test do
# sh "ruby -Ilib:test #{Dir["#{File.dirname(__FILE__)}/../test/poolparty/*/*.rb"].join(" ")}"
# end
Rake::TestTask.new(:test) do |test|
test.libs << 'lib' << 'test'
test.pattern = 'test/**/*_test.rb'
test.verbose = true
end
begin
require 'rcov/rcovtask'
Rcov::RcovTask.new(:rcov) do |t|
t.libs << FileList['lib/**/*.rb']
t.rcov_opts = [
'-xRakefile', '-xrakefile',
'-xlib/erlang',
'--text-report',
'--sort coverage'
] + FileList['tasks/*.rake'].pathmap("-x%p")
t.test_files = FileList['test/lib/**/*_test.rb']
t.output_dir = 'coverage'
t.verbose = true
end
rescue LoadError
puts "RCov is not available"
end
desc "Clean tmp directory"
task :clean_tmp do |t|
FileUtils.rm_rf("#{File.dirname(__FILE__)}/Manifest.txt") if ::File.exists?("#{File.dirname(__FILE__)}/Manifest.txt")
FileUtils.touch("#{File.dirname(__FILE__)}/Manifest.txt")
%w(logs tmp).each do |dir|
FileUtils.rm_rf("#{File.dirname(__FILE__)}/#{dir}") if ::File.exists?("#{File.dirname(__FILE__)}/#{dir}")
end
end
desc "Remove the pkg directory"
task :clean_pkg do |t|
%w(pkg).each do |dir|
FileUtils.rm_rf("#{File.dirname(__FILE__)}/#{dir}") if ::File.exists?("#{File.dirname(__FILE__)}/#{dir}")
end
end
namespace :gem do
desc "Build the gem only if the tests pass"
task :test_then_build => [:test, :build]
desc "Build and install the gem only if the tests pass"
task :test_then_install => [:test, :install]
end
# Generate documentation
Rake::RDocTask.new do |rd|
rd.main = "README.rdoc"
rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
rd.rdoc_dir = "rdoc"
# rd.template = "hanaa"
end
================================================
FILE: Vagrantfile
================================================
Vagrant::Config.run do |config|
# All Vagrant configuration is done here. The most common configuration
# options are documented and commented below. For a complete reference,
# please see the online documentation at vagrantup.com.
# Every Vagrant virtual environment requires a box to build off of.
config.vm.box = "base"
# The url from where the 'config.vm.box' box will be fetched if it
# doesn't already exist on the user's system.
# config.vm.box_url = "http://domain.com/path/to/above.box"
# Boot with a GUI so you can see the screen. (Default is headless)
# config.vm.boot_mode = :gui
# Assign this VM to a host only network IP, allowing you to access it
# via the IP.
config.vm.network "33.33.33.10"
# Forward a port from the guest to the host, which allows for outside
# computers to access the VM, whereas host only networking does not.
# config.vm.forward_port "http", 80, 8080
# Share an additional folder to the guest VM. The first argument is
# an identifier, the second is the path on the guest to mount the
# folder, and the third is the path on the host to the actual folder.
config.vm.share_folder "v-data", "/vagrant_data", ".", :nfs => true
# Enable provisioning with Puppet stand alone. Puppet manifests
# are contained in a directory path relative to this Vagrantfile.
# You will need to create the manifests directory and a manifest in
# the file base.pp in the manifests_path directory.
#
# An example Puppet manifest to provision the message of the day:
#
# # group { "puppet":
# # ensure => "present",
# # }
# #
# # File { owner => 0, group => 0, mode => 0644 }
# #
# # file { '/etc/motd':
# # content => "Welcome to your Vagrant-built virtual machine!
# # Managed by Puppet.\n"
# # }
#
# config.vm.provision :puppet do |puppet|
# puppet.manifests_path = "manifests"
# puppet.manifest_file = "base.pp"
# end
# Enable provisioning with chef solo, specifying a cookbooks path (relative
# to this Vagrantfile), and adding some recipes and/or roles.
#
# config.vm.provision :chef_solo do |chef|
# chef.cookbooks_path = "cookbooks"
# chef.add_recipe "mysql"
# chef.add_role "web"
#
# # You may also specify custom JSON attributes:
# chef.json = { :mysql_password => "foo" }
# end
# Enable provisioning with chef server, specifying the chef server URL,
# and the path to the validation key (relative to this Vagrantfile).
#
# The Opscode Platform uses HTTPS. Substitute your organization for
# ORGNAME in the URL and validation key.
#
# If you have your own Chef Server, use the appropriate URL, which may be
# HTTP instead of HTTPS depending on your configuration. Also change the
# validation key to validation.pem.
#
# config.vm.provision :chef_client do |chef|
# chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
# chef.validation_key_path = "ORGNAME-validator.pem"
# end
#
# If you're using the Opscode platform, your validator client is
# ORGNAME-validator, replacing ORGNAME with your organization name.
#
# IF you have your own Chef Server, the default validation client name is
# chef-validator, unless you changed the configuration.
#
# chef.validation_client_name = "ORGNAME-validator"
end
================================================
FILE: bin/cloud
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../lib")
require "poolparty"
require 'git-style-binary/command'
GitStyleBinary.primary do
# self.class.send :include, Printing
@theme = :short
version "PoolParty release: #{PoolParty.version}"
banner <<-EOS
Usage: #{$0} #{all_options_string} COMMAND [ARGS]
The cloud subcommands commands are:
\#{GitStyleBinary.pretty_known_subcommands(:short).join(" ")}
See 'cloud help COMMAND' for more information on a specific command.
EOS
opt :clouds_dot_rb, "Set the clouds.rb file", :type => String, :default => "clouds.rb"
opt :very_verbose, "Set very verbose mode on", :type => :boolean, :default => false
opt :debug, "Debug the output", :type => :boolean, :default => false
opt :very_debug, "Set very debug mode on", :type => :boolean, :default => false
opt :name, "Name of the working cloud", :type => String, :default => nil
opt :chef_task, "Name of chef task to execute", :type => String, :default => 'default'
before_run do |command|
# Setup testing/debugging
$PP_VERBOSE = true if command[:verbose]
$VERY_VERBOSE = true if command[:very_verbose]
$DEBUGGING = true if command[:debug]
$VERY_DEBUGGING = true if command[:very_debug]
begin
require command[:clouds_dot_rb]
rescue LoadError => e
puts e
puts e.backtrace
puts "Failed loading #{command[:clouds_dot_rb]}, try using -c to specify the location of your clouds.rb"
exit
end
pool.chef_step command[:chef_task].to_sym
@loaded_pool = pool
@loaded_clouds = command[:name] ? [pool.clouds[command[:name]]] : pool.clouds.map {|name,cld|cld}
if @loaded_clouds.size == 0
puts "No clouds loaded. Check your clouds.rb or -n option"
exit
end
end
run do |command|
subcommands = GitStyleBinary.list_subcommands
puts "cloud %s" % PoolParty.version
puts "Usage: cloud COMMAND [ARGS]
The cloud subcommands commands are:
#{subcommands}
See 'cloud help COMMAND' for more information on a specific command"
end
end
================================================
FILE: bin/cloud-bootstrap
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../lib")
require "poolparty"
require 'git-style-binary/command'
GitStyleBinary.command do
banner <<-EOS
Usage: #{$0} #{all_options_string}
Bootstrap a cloud instance
EOS
short_desc "Bootstrap a cloud instance"
opt :inst_num, "The number of the instance to run bootstrap on", :type => :int
opt :force, "Force rebootstrapping (useful for upgrading)", :default => false
run do |command|
@loaded_clouds.each do |cld|
cld.compile!
cld.bootstrap!
end
end
end
================================================
FILE: bin/cloud-compile
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../lib")
require "poolparty"
require 'git-style-binary/command'
GitStyleBinary.command do
banner <<-EOS
Usage: #{$0} #{all_options_string}
Configure a cloud instance
EOS
short_desc "Configure a cloud instance"
opt :inst_num, "The number of the instance to run bootstrap on", :type => :int
run do |command|
@loaded_clouds.each do |cld|
ENV["POOLPARTY_NO_VALIDATION"] = "true"
cld.compile!
end
end
end
================================================
FILE: bin/cloud-configure
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../lib")
require "poolparty"
require 'git-style-binary/command'
GitStyleBinary.command do
banner <<-EOS
Usage: #{$0} #{all_options_string}
Configure a cloud instance
EOS
short_desc "Configure a cloud instance"
opt :inst_num, "The number of the instance to run bootstrap on", :type => :int
opt :generate_graph, "Generate the dependency tree graph", :type => :string, :default => nil
run do |command|
@loaded_clouds.each do |cld|
cld.configure!
end
end
end
================================================
FILE: bin/cloud-console
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../lib")
require "poolparty"
require 'git-style-binary/command'
GitStyleBinary.command do
banner <<-EOS
Usage: #{$0} #{all_options_string}
Open an irb session with the clouds.rb loaded
EOS
short_desc "Open an irb session with clouds.rb loaded"
opt :inst_num, "The number of the instance to run bootstrap on", :type => :int
run do |command|
puts "Loading PoolParty console..."
#exec "#{irb} #{libs} --simple-prompt"
require 'irb/completion'
require command[:clouds_dot_rb]
require "#{File.dirname(__FILE__) + '/../lib/poolparty.rb'}"
require 'irb'
pool.auto_execute = false
IRB.setup(nil)
IRB.conf[:IRB_NAME]="cloud"
IRB.conf[:PROMPT_MODE]=:CLOUD
IRB.conf[:PROMPT][:CLOUD]={
:PROMPT_I => "%N> ",
:PROMPT_S => "%l> ",
:PROMPT_C => "* ",
:PROMPT_N => ">> ",
:RETURN => "=>%s\n" }
irb=IRB::Irb.new
IRB.conf[:IRB_RC].call(irb.context) if IRB.conf[:IRB_RC]
IRB.conf[:MAIN_CONTEXT] = irb.context
trap("SIGINT") do
irb.signal_handle
end
catch(:IRB_EXIT) do
irb.eval_input
end
end
end
================================================
FILE: bin/cloud-contract
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../lib")
require "poolparty"
require 'git-style-binary/command'
GitStyleBinary.command do
banner <<-EOS
Usage: #{$0} #{all_options_string}
contracts the cloud by a single instance
EOS
short_desc "contracts the cloud by a single instance"
run do |command|
@loaded_clouds.each do |cld|
node_to_terminate = cld.nodes.last
# node_to_terminate.cloud = cld
puts cld.nodes.size
if cld.nodes.size - 1 < cld.minimum_instances
puts "Contracting instances by 1 in cloud #{cld.name} will lower the number of instances below specified minimum (#{cld.minimum_instances})"
next
end
msg = [
"Contracting cloud #{cld.name} (#{cld.keypair}) by 1",
"#{cld.nodes.size} running instances (#{cld.minimum_instances} - #{cld.maximum_instances})",
" node: #{node_to_terminate.public_ip}"
]
puts msg.join("\n")
node_to_terminate.terminate!
puts "#{node_to_terminate.public_ip} has been terminated"
end
end
end
================================================
FILE: bin/cloud-cssh
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../lib")
require "poolparty"
require 'git-style-binary/command'
GitStyleBinary.command do
banner <<-EOS
Usage: #{$0} #{all_options_string}
cssh into the cloud
EOS
short_desc "cssh into the cloud"
opt :user, "the user name", :type => :string, :default => nil
opt :use_keypair, "whether or not to use the default keypair", :type => :integer, :default => 0
run do |command|
cloud = @loaded_clouds.first
user = command[:user]
keyp = command[:use_keypair]
cloud.cssh( user, keyp != 0 )
end
end
================================================
FILE: bin/cloud-expand
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../lib")
require "poolparty"
require 'git-style-binary/command'
GitStyleBinary.command do
banner <<-EOS
Usage: #{$0} #{all_options_string}
expands the cloud by a single instance
EOS
short_desc "expands the cloud by a single instance"
run do |command|
@loaded_clouds.each do |cld|
msg = [
"Expanding cloud #{cld.name} (#{cld.keypair})",
"#{cld.nodes.size} running instances (#{cld.minimum_instances} - #{cld.maximum_instances})"
]
print_msg(msg)
inst = cld.expand_by(1)
msg = [
"Your cloud has expanded by node: #{inst.public_ip}"
]
print_msg(msg)
end
end
end
================================================
FILE: bin/cloud-list
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../lib")
require "poolparty"
require 'git-style-binary/command'
GitStyleBinary.command do
banner <<-EOS
Usage: #{$0} #{all_options_string}
List the clouds and their nodes
e.g. show a list of the current instances on the cloud
EOS
opt :instance_id, "Describe a specific instance", :type => String, :default => nil
short_desc "show a list of the current instances on the clouds"
run do |command|
puts "#{pool.name}"
puts "==="
@loaded_clouds.each do |cld|
if command[:instance_id]
require 'pp'
pp result = cld.nodes(:instance_id=>command[:instance_id])
else
msg = ["* #{cld.name} cloud, #{cld.image_id}, #{cld.instance_type}"]
hsh = {}
cld.all_nodes.each do |n|
(hsh[n.status] ||= []) << n
end
hsh.each do |k,v|
msg << " - #{k} nodes"
v.each do |a|
msg << " - #{a.instance_id}\t#{a.status}\t#{a.public_ip}\t#{a.private_ip}\t#{a.zone}"
end
msg << ""
end
puts msg
end
end
end
end
================================================
FILE: bin/cloud-reboot
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../lib")
require "poolparty"
require 'git-style-binary/command'
GitStyleBinary.command do
banner <<-EOS
Usage: #{$0} #{all_options_string}
Cycle through nodes in the cloud and wait for the autoscaler to reboot
EOS
short_desc "Cycle the instances"
run do |command|
@loaded_clouds.each do |cld|
cld.reboot!
end
end
end
================================================
FILE: bin/cloud-rsync
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../lib")
require "poolparty"
require 'git-style-binary/command'
GitStyleBinary.command do
banner <<-EOS
Usage: #{$0} #{all_options_string}
ssh into an instance of the cloud
EOS
short_desc "ssh into an instance of the cloud"
opt :to, "To this directory", :default => "~"
run do |command|
cloud = @loaded_clouds.first
dir = command.argv.pop
if !cloud.nodes.empty?
cloud.rsync(dir, command[:to])
else
puts "No running instances can be found"
end
end
end
================================================
FILE: bin/cloud-run
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../lib")
require "poolparty"
require 'git-style-binary/command'
GitStyleBinary.command do
banner <<-EOS
Usage: #{$0} #{all_options_string}
Run a command on all running instances
EOS
opt :instance_id, "Run only on a specific instance", :type => String, :default => nil
opt :command, "Command to run", :type => String, :default => nil
short_desc "Run a command on all running instances"
run do |command|
puts "Running command #{command[:command]} on all nodes"
@loaded_clouds.each do |cld|
output = cld.nodes.map do |n|
n.ssh(command[:command])
end
#p output
end
end
end
================================================
FILE: bin/cloud-show
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../lib")
require "poolparty"
require 'git-style-binary/command'
GitStyleBinary.command do
@theme = :short
version "PoolParty #{$0} command"
banner <<-EOS
Usage: #{$0} #{all_options_string}
shows output about the clouds.rb
EOS
short_desc "shows output about the clouds.rb"
run do |command|
@loaded_clouds.each do |cld|
msg = [
"Cloud: #{cld.name}",
"----------------------------",
"Running Instances: #{cld.nodes.size}",
"Minimum instances: #{cld.minimum_instances}",
"Maximum instances: #{cld.maximum_instances}",
"Running on: #{cld.cloud_provider.name}",
"Keypair: #{cld.keypair.basename}",
"Security group: #{cld.cloud_provider.security_group_names.join(', ')}",
"Availability zones: #{cld.cloud_provider.availability_zones.join(', ')}",
"User: #{cld.user}",
"Active recipes: #{cld.chef._recipes(cld.pool.chef_step).join ", " }"
]
if cld.load_balancers.size > 0
load_balancers = cld.cloud_provider.load_balancers.first.running_load_balancers.map {|a| a[:dns_name]}
msg << "Load balancers: #{load_balancers.join("\n\t\t\t")}"
end
if cld.rds_instances.size > 0
available = cld.cloud_provider.available_rds_instances.map{|r| "#{r.instance_id}\t#{r.current_status.Endpoint.Address}" }
available.unshift 'RDS Instances:'
msg << available.join("\n\t\t\t")
end
puts msg.flatten
end
end
end
================================================
FILE: bin/cloud-ssh
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../lib")
require "poolparty"
require 'git-style-binary/command'
GitStyleBinary.command do
banner <<-EOS
Usage: #{$0} #{all_options_string}
ssh into an instance of the cloud
EOS
short_desc "ssh into an instance of the cloud"
opt :instance_number, "Ssh into the instance number", :type => :integer, :default => 0
run do |command|
cloud = @loaded_clouds.first
if !cloud.nodes.empty?
inst = cloud.nodes[command[:instance_number]]
inst ? inst.ssh : puts("Error: The instance number (#{command[:instance_number]}) is too high for the current number of instances")
else
puts "No running instances can be found"
end
end
end
================================================
FILE: bin/cloud-start
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../lib")
require "poolparty"
require 'git-style-binary/command'
GitStyleBinary.command do
banner <<-EOS
Usage: #{$0} #{all_options_string}
starts a single instance in your cloud.
EOS
short_desc "starts a single instance in your cloud"
run do |command|
@loaded_clouds.each do |cld|
cld.run
end
end
end
================================================
FILE: bin/cloud-terminate
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../lib")
require "poolparty"
require 'git-style-binary/command'
GitStyleBinary.command do
banner <<-EOS
Usage: #{$0} #{all_options_string} COMMAND [ARGS]
terminate the cloud
EOS
short_desc "terminate the cloud"
opt :yes, "Answer yes to the are you sure question", :type => :boolean, :default => false
run do |command|
@loaded_clouds.each do |cld|
msg = [
"Shutting down #{cld.name} cloud",
"Are you sure? (Y/N)"
]
o = command[:yes] || true
if o
puts "Shutting down cloud"
cld.teardown
else
end
end
end
end
================================================
FILE: bin/cloud-vnc
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../lib")
require "poolparty"
require 'git-style-binary/command'
GitStyleBinary.command do
banner <<-EOS
Usage: #{$0} #{all_options_string}
start a vnc server and create an ssh tunnel
EOS
short_desc "ssh into an instance of the cloud"
opt :instance_number, "Start VNC server on this instance number", :type => :integer, :default => 0
opt :local_port, "The local port to tunnel to", :type => :integer, :default => 5900
opt :display, "The remote display number to start the VNC Server on", :type => :integer, :default => 0
run do |command|
cloud = @loaded_clouds.first
if !cloud.nodes.empty?
inst = cloud.nodes[command[:instance_number]]
if inst
inst.ssh(["x11vnc -localhost -nopw -display :#{command[:display]}"],
{'-X' => nil,
'-C' => nil,
' -o' => 'UserKnownHostsFile=/dev/null',
'-L' => "#{command[:local_port]}:localhost:5900"})
else
puts("Error: The instance number (#{command[:instance_number]}) is too high for the current number of instances")
end
else
puts "No running instances can be found"
end
end
end
================================================
FILE: config/requirements.rb
================================================
require 'fileutils'
include FileUtils
require 'rubygems'
require 'rake'
$:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
================================================
FILE: examples/README.md
================================================
simple.rb
===
The simple.rb configuration will create an ec2 auto scaling group that will launch the minimum_instances (1 in this case,) inside a security group. The default naming of most objects, such as security_groups, auto scaling groups, launch_configurations and load_balancers and keypairs follow the poolname-cloudname convention. For example, the cloud below will create a security-group named 'pooolparty-simple'. The names can be overridden, important if you want more than one. If a keypair does not exist, it will be created.
Since auto scaling is specified in this cloud, poolparty will not start the instance directly. Instead, poolparty will create an ec2_launch_configuration and auto scaling group.
Some important notes: Some of the methods used inside the cloud block are specific to the cloud provider being used, ec2 in this example. The cloud_provider methods are added when the cloud_provider is specified, therefore you must be sure to specify the cloud_provider before any cloud_provider specific methods.
User_data. While not a specific feature of poolparty, we often use user_data scripts to configure instance in the cloud. This allows for separation of concerns:
- PoolParty takes care of setting up an infrastructure of firewalled autoscaling, load balanced instances across multiple data centers.
- Userdata scripts configure the instances, optionally pulling in from a repository of additional tasks. Userdata script can be used in conjunction with [runrul](http://alestic.com/2009/08/runurl) to run scripts form a shared repository, even installing and run chef if desired.
A benefit of userdata scripted instance configuration is that it does not require active management and monitoring. You just define your infrastructure with poolparty and let the autoscaling take care of the rest.
chef_cloud.rb
===
The chef_cloud.rb does not define an autoscaling group. Since there is no autoscaling group, poolparty will launch the instances (ec2_run_instance.) and wait for ssh to be available on all the launched hosts.
Next, if a chef-repo was specified:
1. The repo will be uploaded the /etc/chef on the instances.
2. Ssh to the instances and call chef -c /etc/chef/solo.rb -j /etc/chef/dna.json
The dna.json can be populated either by simply editing the file dna.json in your chef-repo, or you can specify chef_attributes in your cloud block. for example:
chef_attributes :apache2 => {:listen_ports => ["80", "8080"]} "8080"]}
If you specify chef_attributes they will be compiled into a a role namded for the cloud at /etc/chef/roles/cloudname.json, and a dna.json file that will execute the clouds role on the instance.
Notes, Tips and Comments:
===
The goal of having an completely idempotent declarative configuration system is particularly valuable when you need to manage and update running servers. In the cloud we have found that often, it is easier to simply throw away the server and start a new one from a fresh state.
1. Update userdata scripts and launch configs
2. Terminate the instances (1 at a time for a rolling restart, or all at once)
3. Let the autoscaling (or client side poolparty) start new instances with the new userdata
Since the instances are always starting from a known state (a base ami) the configuration scripts can be much simpler, simple shell scripts, possibly executed as runurls.
If runurls are being called from the userdata script, another way to update the cloud instances configuration is to update the runurl (if you are hosting it, which is recommended) you can terminate instances and the newly launched instances will use the updated runurl/
We try and keep private data out of the runurl scripts, opting to supply this data either as environment variables (possibly set by the userdata script,) as command line arguments to runrul (i.e. $1, $2 etc), or variables contained in a separate file our userdata script can download.
When the runurl cannot be public, and we do not want to embed all the runurl scripts in the userdata, we have used an authenticate only aws user. That is create an aws user, but do not provide a credit card or sign up for any services. Even tho the user can not create any resources, it can authenticate. We put the authenticate only user credentials in the userdata file, and a small bit of code at the top of our userdata file to fetch and install an s3 utility, such as s3cmd. Then the s3cmd can download private readonly s3 urls.
chef
---
If you want to use chef, one way to do so is to put code like the following in your userdata
# OPTIONAL: install chef-solo
export CLOUD=${1:-'poolparty'}
sudo gem install ohai chef --source http://gems.opscode.com --no-rdoc --no-ri
mkdir -p /etc/chef/cookpooks
s3sync -r https://mybucket.s3.amazonaws.com/chef-repo /etc/chef/chef
s3sync https://cloudteam.s3.amazonaws.com/chef-repo/$CLOUD.json /etc/chef/dna.json
chef-solo -c /etc/chef/chef/solo.rb -j /etc/chef/chef/dna.json
================================================
FILE: examples/chef_cloud/chef_repo/.gitignore
================================================
.rake_test_cache
================================================
FILE: examples/chef_cloud/chef_repo/README
================================================
Began as a clone of the Opscode base repo
http://wiki.opscode.com/display/chef/Chef+Repository
================================================
FILE: examples/chef_cloud/chef_repo/Rakefile
================================================
#
# Rakefile for Chef Server Repository
#
# Author:: Adam Jacob ()
# Copyright:: Copyright (c) 2008 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# 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 'rubygems'
require 'chef'
require 'json'
# Make sure you have loaded constants first
require File.join(File.dirname(__FILE__), 'config', 'rake')
# And choosen a VCS
if File.directory?(File.join(TOPDIR, ".svn"))
$vcs = :svn
elsif File.directory?(File.join(TOPDIR, ".git"))
$vcs = :git
end
load 'chef/tasks/chef_repo.rake'
================================================
FILE: examples/chef_cloud/chef_repo/certificates/README
================================================
This directory contains certificates created by the Rakefile.
================================================
FILE: examples/chef_cloud/chef_repo/config/client.rb.example
================================================
#
# Example Chef Client Config File
#
# We recommend using Opscode's chef cookbook for managing chef itself,
# instead of using this file. It is provided as an example.
log_level :info
log_location STDOUT
ssl_verify_mode :verify_none
registration_url "https://chef.localdomain"
openid_url "https://chef.localdomain"
template_url "https://chef.localdomain"
remotefile_url "https://chef.localdomain"
search_url "https://chef.localdomain"
role_url "https://chef.localdomain"
file_store_path "/srv/chef/file_store"
file_cache_path "/srv/chef/cache"
pid_file "/var/run/chef/chef-client.pid"
Chef::Log::Formatter.show_time = true
================================================
FILE: examples/chef_cloud/chef_repo/config/rake.rb
================================================
###
# Company and SSL Details
###
# The company name - used for SSL certificates, and in srvious other places
COMPANY_NAME = "AT&T Interactive"
# The Country Name to use for SSL Certificates
SSL_COUNTRY_NAME = "US"
# The State Name to use for SSL Certificates
SSL_STATE_NAME = "CA"
# The Locality Name for SSL - typically, the city
SSL_LOCALITY_NAME = "Glendale"
# What department?
SSL_ORGANIZATIONAL_UNIT_NAME = "Applied Research"
# The SSL contact email address
SSL_EMAIL_ADDRESS = "cloudcomputing@attinteractive.com"
# License for new Cookbooks
# Can be :apachev2 or :none
NEW_COOKBOOK_LICENSE = :apachev2
##########################
# Chef Repository Layout #
##########################
# Where to install upstream cookbooks for serving
COOKBOOK_PATH = "/srv/chef/cookbooks"
# Where to install site-local modifications to upstream cookbooks
SITE_COOKBOOK_PATH = "/srv/chef/site-cookbooks"
# Where to install roles
ROLE_PATH = "/srv/chef/roles"
# Chef Config Path
CHEF_CONFIG_PATH = "/etc/chef"
# The location of the Chef Server Config file (on the server)
CHEF_SERVER_CONFIG = File.join(CHEF_CONFIG_PATH, "server.rb")
# The location of the Chef Client Config file (on the client)
CHEF_CLIENT_CONFIG = File.join(CHEF_CONFIG_PATH, "client.rb")
###
# Useful Extras (which you probably don't need to change)
###
# The top of the repository checkout
TOPDIR = File.expand_path(File.join(File.dirname(__FILE__), ".."))
# Where to store certificates generated with ssl_cert
CADIR = File.expand_path(File.join(TOPDIR, "certificates"))
# Where to store the mtime cache for the recipe/template syntax check
TEST_CACHE = File.expand_path(File.join(TOPDIR, ".rake_test_cache"))
================================================
FILE: examples/chef_cloud/chef_repo/config/server.rb.example
================================================
#
# Example Chef Server Config File
#
# We recommend using Opscode's chef cookbook for managing chef itself,
# instead of using this file. It is provided as an example.
log_level :info
log_location "/var/log/chef/server.log"
ssl_verify_mode :verify_none
registration_url "https://localhost"
openid_url "https://localhost"
template_url "https://localhost"
remotefile_url "https://localhost"
search_url "https://localhost"
role_url "https://localhost"
#validation_token "change_this_token"
cookbook_path [ "/srv/chef/site-cookbooks", "/srv/chef/cookbooks" ]
file_store_path "/srv/chef/file_store"
file_cache_path "/srv/chef/cache"
node_path "/srv/chef/nodes"
openid_store_path "/srv/chef/openid/store"
openid_cstore_path "/srv/chef/openid/cstore"
search_index_path "/srv/chef/search_index"
# uncomment and modify to allow specific openid providers
#openid_providers [ "https://localhost", "https://chef" ]
# uncomment and modify to lock down openid identifiers, ie 'some_user.myopenid.com'
#authorized_openid_identifiers [ "" ]
Chef::Log::Formatter.show_time = true
================================================
FILE: examples/chef_cloud/chef_repo/config/solo.rb.example
================================================
#
# Chef Solo Config File
#
log_level :info
log_location STDOUT
file_cache_path "/srv/chef/cookbooks"
ssl_verify_mode :verify_none
Chef::Log::Formatter.show_time = false
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/README
================================================
This directory contains upstream or shared cookbooks.
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/README.rdoc
================================================
= 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.
= 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.
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/attributes/apache.rb
================================================
#
# 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"
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/definitions/apache_conf.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/definitions/apache_module.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/definitions/apache_site.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/definitions/web_app.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/files/default/apache2_module_conf_generate.pl
================================================
#!/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;
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/metadata.json
================================================
{
"description": "Installs and configures all aspects of apache2 using Debian style symlinks with helper definitions",
"replacing": {
},
"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_authz_groupfile": "Apache module 'authz_groupfile'",
"apache2::mod_dav": "Apache module 'dav'",
"apache2::mod_auth_basic": "Apache module 'auth_basic'",
"apache2::mod_setenvif": "Apache module 'setenvif' with config file",
"apache2": "",
"apache2::mod_authz_user": "Apache module 'authz_user'",
"apache2::mod_deflate": "Apache module 'deflate' with config file",
"apache2::mod_dav_svn": "Apache module 'dav_svn'",
"apache2::mod_negotiation": "Apache module 'negotiation' with config file",
"apache2::mod_ssl": "Apache module 'ssl' with config file, adds port 443 to listen_ports",
"apache2::mod_authz_host": "Apache module 'authz_host'",
"apache2::mod_rewrite": "Apache module 'rewrite'",
"apache2::mod_auth_digest": "Apache module 'auth_digest'",
"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_env": "Apache module 'env'",
"apache2::mod_headers": "Apache module 'headers'",
"apache2::mod_authnz_ldap": "Apache module 'authnz_ldap'",
"apache2::mod_autoindex": "Apache module 'autoindex' with config file",
"apache2::mod_proxy": "Apache module 'proxy' with config file",
"apache2::mod_proxy_connect": "Apache module 'proxy_connect'",
"apache2": "Main Apache configuration",
"apache2::mod_alias": "Apache module 'alias' with config file",
"apache2::mod_ldap": "Apache module 'ldap'",
"apache2::mod_status": "Apache module 'status' with config file",
"apache2::mod_authz_default": "Apache module 'authz_default'",
"apache2::mod_log_config": "Apache module 'log_config'",
"apache2::god_monitor": "",
"apache2::mod_expires": "Apache module 'expires'",
"apache2::mod_proxy_http": "Apache module 'proxy_http'",
"apache2::mod_mime": "Apache module 'mime' with config file",
"apache2::mod_proxy_ajp": "Apache module 'proxy_ajp'"
},
"platforms": {
"ubuntu": [
],
"centos": [
],
"debian": [
],
"redhat": [
]
},
"maintainer": "Opscode, Inc.",
"version": "0.9.1",
"recommendations": {
},
"name": "apache2",
"maintainer_email": "cookbooks@opscode.com",
"attributes": {
"apache\/traceenable": {
"default": "On",
"type": "string",
"multiple_values": false,
"description": "Determine behavior of TRACE requests",
"display_name": "Apache Trace Enable",
"required": false,
"recipes": [
]
},
"apache\/timeout": {
"default": "300",
"type": "string",
"multiple_values": false,
"description": "Connection timeout value",
"display_name": "Apache Timeout",
"required": false,
"recipes": [
]
},
"apache\/icondir": {
"default": "\/usr\/share\/apache2\/icons",
"type": "string",
"multiple_values": false,
"description": "Directory location for icons",
"display_name": "Apache Icondir",
"required": false,
"recipes": [
]
},
"apache\/user": {
"default": "www-data",
"type": "string",
"multiple_values": false,
"description": "User Apache runs as",
"display_name": "Apache User",
"required": false,
"recipes": [
]
},
"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",
"required": false,
"recipes": [
]
},
"apache\/worker\/maxclients": {
"default": "1024",
"type": "string",
"multiple_values": false,
"description": "Maximum number of simultaneous connections",
"display_name": "Apache Worker MPM MaxClients",
"required": false,
"recipes": [
]
},
"apache\/worker": {
"type": "hash",
"multiple_values": false,
"description": "Hash of Apache prefork tuning attributes.",
"display_name": "Apache Worker",
"required": false,
"recipes": [
]
},
"apache\/contact": {
"default": "ops@example.com",
"type": "string",
"multiple_values": false,
"description": "Email address of webmaster",
"display_name": "Apache Contact",
"required": false,
"recipes": [
]
},
"apache\/prefork\/startservers": {
"default": "16",
"type": "string",
"multiple_values": false,
"description": "Number of MPM servers to start",
"display_name": "Apache Prefork MPM StartServers",
"required": false,
"recipes": [
]
},
"apache\/prefork\/minspareservers": {
"default": "16",
"type": "string",
"multiple_values": false,
"description": "Minimum number of spare server processes",
"display_name": "Apache Prefork MPM MinSpareServers",
"required": false,
"recipes": [
]
},
"apache\/keepalivetimeout": {
"default": "5",
"type": "string",
"multiple_values": false,
"description": "Time to wait for requests on persistent connection",
"display_name": "Apache Keepalive Timeout",
"required": false,
"recipes": [
]
},
"apache\/keepaliverequests": {
"default": "100",
"type": "string",
"multiple_values": false,
"description": "Number of requests allowed on a persistent connection",
"display_name": "Apache Keepalive Requests",
"required": false,
"recipes": [
]
},
"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",
"required": false,
"recipes": [
]
},
"apache\/listen_ports": {
"default": [
"80",
"443"
],
"type": "array",
"multiple_values": false,
"description": "Ports that Apache should listen on",
"display_name": "Apache Listen Ports",
"required": false,
"recipes": [
]
},
"apache\/dir": {
"default": "\/etc\/apache2",
"type": "string",
"multiple_values": false,
"description": "Location for Apache configuration",
"display_name": "Apache Directory",
"required": false,
"recipes": [
]
},
"apache\/worker\/maxsparethreads": {
"default": "192",
"type": "string",
"multiple_values": false,
"description": "Maximum number of spare worker threads",
"display_name": "Apache Worker MPM MaxSpareThreads",
"required": false,
"recipes": [
]
},
"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",
"required": false,
"recipes": [
]
},
"apache\/prefork\/serverlimit": {
"default": "400",
"type": "string",
"multiple_values": false,
"description": "Upper limit on configurable server processes",
"display_name": "Apache Prefork MPM ServerLimit",
"required": false,
"recipes": [
]
},
"apache\/binary": {
"default": "\/usr\/sbin\/apache2",
"type": "string",
"multiple_values": false,
"description": "Apache server daemon program",
"display_name": "Apache Binary",
"required": false,
"recipes": [
]
},
"apache\/prefork\/maxspareservers": {
"default": "32",
"type": "string",
"multiple_values": false,
"description": "Maximum number of spare server processes",
"display_name": "Apache Prefork MPM MaxSpareServers",
"required": false,
"recipes": [
]
},
"apache\/keepalive": {
"default": "On",
"type": "string",
"multiple_values": false,
"description": "HTTP persistent connections",
"display_name": "Apache Keepalive",
"required": false,
"recipes": [
]
},
"apache": {
"type": "hash",
"multiple_values": false,
"description": "Hash of Apache attributes",
"display_name": "Apache Hash",
"required": false,
"recipes": [
]
},
"apache\/worker\/startservers": {
"default": "4",
"type": "string",
"multiple_values": false,
"description": "Initial number of server processes to start",
"display_name": "Apache Worker MPM StartServers",
"required": false,
"recipes": [
]
},
"apache\/prefork\/maxclients": {
"default": "400",
"type": "string",
"multiple_values": false,
"description": "Maximum number of simultaneous connections",
"display_name": "Apache Prefork MPM MaxClients",
"required": false,
"recipes": [
]
},
"apache\/prefork": {
"type": "hash",
"multiple_values": false,
"description": "Hash of Apache prefork tuning attributes.",
"display_name": "Apache Prefork",
"required": false,
"recipes": [
]
},
"apache\/servertokens": {
"default": "Prod",
"type": "string",
"multiple_values": false,
"description": "Server response header",
"display_name": "Apache Server Tokens",
"required": false,
"recipes": [
]
},
"apache\/worker\/minsparethreads": {
"default": "64",
"type": "string",
"multiple_values": false,
"description": "Minimum number of spare worker threads",
"display_name": "Apache Worker MPM MinSpareThreads",
"required": false,
"recipes": [
]
},
"apache\/serversignature": {
"default": "On",
"type": "string",
"multiple_values": false,
"description": "Configure footer on server-generated documents",
"display_name": "Apache Server Signature",
"required": false,
"recipes": [
]
},
"apache\/log_dir": {
"default": "\/etc\/apache2",
"type": "string",
"multiple_values": false,
"description": "Location for Apache logs",
"display_name": "Apache Log Directory",
"required": false,
"recipes": [
]
}
},
"suggestions": {
},
"license": "Apache 2.0",
"conflicting": {
},
"dependencies": {
},
"providing": {
"apache2::mod_python": [
],
"apache2::mod_authn_file": [
],
"apache2::mod_dir": [
],
"apache2::mod_php5": [
],
"apache2::mod_proxy_balancer": [
],
"apache2::mod_authz_groupfile": [
],
"apache2::mod_dav": [
],
"apache2::mod_auth_basic": [
],
"apache2::mod_setenvif": [
],
"apache2::mod_authz_user": [
],
"apache2::mod_deflate": [
],
"apache2::mod_dav_svn": [
],
"apache2::mod_negotiation": [
],
"apache2::mod_ssl": [
],
"apache2::mod_authz_host": [
],
"apache2::mod_rewrite": [
],
"apache2::mod_auth_digest": [
],
"apache2::mod_cgi": [
],
"apache2::mod_fcgid": [
],
"apache2::mod_env": [
],
"apache2::mod_headers": [
],
"apache2::mod_authnz_ldap": [
],
"apache2::mod_autoindex": [
],
"apache2::mod_proxy": [
],
"apache2::mod_proxy_connect": [
],
"apache2::mod_alias": [
],
"apache2::mod_ldap": [
],
"apache2::mod_status": [
],
"apache2::mod_authz_default": [
],
"apache2::mod_log_config": [
],
"apache2": [
],
"apache2::god_monitor": [
],
"apache2::mod_expires": [
],
"apache2::mod_proxy_http": [
],
"apache2::mod_mime": [
],
"apache2::mod_proxy_ajp": [
]
},
"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= 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"
}
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/metadata.rb
================================================
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.9.1"
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_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/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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/default.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/god_monitor.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_alias.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_auth_basic.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_auth_digest.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_authn_file.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_authnz_ldap.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_authz_default.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_authz_groupfile.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_authz_host.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_authz_user.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_autoindex.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_cgi.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_dav.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_dav_svn.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_deflate.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_dir.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_env.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_expires.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_fcgid.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_headers.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_ldap.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_log_config.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_mime.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_negotiation.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_php5.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_proxy.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_proxy_ajp.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_proxy_balancer.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_proxy_connect.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_proxy_http.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_python.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_rails.rb
================================================
#
# Cookbook Name:: passenger
# Recipe:: default
#
# Author:: Joshua Timberman ()
# Author:: Joshua Sierles ()
# Author:: Michael Hale ()
#
# Copyright:: 2009, Opscode, Inc
# Copyright:: 2009, 37signals
# Coprighty:: 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 "passenger"
template "#{node[:apache][:dir]}/mods-available/passenger.load" do
cookbook "passenger"
source "passenger.load.erb"
owner "root"
group "root"
mode 0755
end
template "#{node[:apache][:dir]}/mods-available/passenger.conf" do
cookbook "passenger"
source "passenger.conf.erb"
owner "root"
group "root"
mode 0755
end
apache_module "passenger"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_rewrite.rb
================================================
#
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_setenvif.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_ssl.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/recipes/mod_status.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/a2dismod.erb
================================================
#!/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."
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/a2dissite.erb
================================================
#!/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."
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/a2enmod.erb
================================================
#!/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."
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/a2ensite.erb
================================================
#!/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."
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/apache2.conf.erb
================================================
#
# 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/
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/apache2.god.erb
================================================
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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/charset.erb
================================================
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/default-site.erb
================================================
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 -%>
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/mods/README
================================================
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.
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/mods/alias.conf.erb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/mods/autoindex.conf.erb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/mods/deflate.conf.erb
================================================
AddOutputFilterByType DEFLATE text/html text/plain text/xml
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/mods/dir.conf.erb
================================================
DirectoryIndex index.html index.cgi index.pl index.php index.xhtml index.htm
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/mods/fcgid.conf.erb
================================================
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 -%>
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/mods/mime.conf.erb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/mods/negotiation.conf.erb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/mods/proxy.conf.erb
================================================
#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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/mods/setenvif.conf.erb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/mods/ssl.conf.erb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/mods/status.conf.erb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/port_apache.erb
================================================
# Port <%= @port %>
-A FWR -p tcp -m tcp --dport <%= @port %> -j ACCEPT
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/ports.conf.erb
================================================
#This file generated via template by Chef.
<% @apache_listen_ports.each do |port| -%>
Listen <%= port %>
NameVirtualHost *:<%= port %>
<% end -%>
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/security.erb
================================================
#
# 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] %>
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apache2/templates/default/web_app.conf.erb
================================================
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]
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apt/files/default/apt-cacher
================================================
# 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 "
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apt/files/default/apt-cacher.conf
================================================
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apt/files/default/apt-proxy-v2.conf
================================================
[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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apt/metadata.json
================================================
{
"description": "Configures apt and apt services",
"replacing": {
},
"recipes": {
"apt::proxy": "Set up an APT proxy",
"apt": "",
"apt::cacher": "Set up an APT cache"
},
"platforms": {
"ubuntu": [
],
"debian": [
]
},
"maintainer": "Opscode, Inc.",
"version": "0.7.0",
"recommendations": {
},
"name": "apt",
"maintainer_email": "cookbooks@opscode.com",
"attributes": {
},
"suggestions": {
},
"license": "Apache 2.0",
"conflicting": {
},
"dependencies": {
},
"providing": {
"apt::proxy": [
],
"apt": [
],
"apt::cacher": [
]
},
"long_description": ""
}
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apt/metadata.rb
================================================
maintainer "Opscode, Inc."
maintainer_email "cookbooks@opscode.com"
license "Apache 2.0"
description "Configures apt and apt services"
version "0.7"
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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apt/recipes/cacher.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apt/recipes/default.rb
================================================
#
# 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.
#
execute "apt-get-update" do
command "apt-get update"
end
%w{/var/cache/local /var/cache/local/preseeding}.each do |dirname|
directory dirname do
owner "root"
group "root"
mode 0644
action :create
end
end
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/apt/recipes/proxy.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/bootstrap/README.rdoc
================================================
= 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-repo/bootstrap.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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/bootstrap/attributes/bootstrap.rb
================================================
#
# 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)] }
default[:bootstrap][:chef][:url_type] = "http"
default[:bootstrap][:chef][:init_style] = "runit"
default[:bootstrap][:chef][:path] = "/srv/chef"
default[:bootstrap][:chef][:run_path] = "/var/run/chef"
default[:bootstrap][:chef][:cache_path] = "/#{bootstrap[:chef][:path]}/cache"
default[:bootstrap][:chef][:serve_path] = "/srv/chef"
default[:bootstrap][:chef][:server_version] = "0.7.10"
default[:bootstrap][:chef][:client_version] = "0.7.10"
default[:bootstrap][:chef][:client_interval] = "1800"
default[:bootstrap][:chef][:client_splay] = "20"
default[:bootstrap][:chef][:log_dir] = "/var/log/chef"
case bootstrap[:chef][:init_style]
when "runit"
default[:bootstrap][:chef][:client_log] = "STDOUT"
default[:bootstrap][:chef][:server_log] = "STDOUT"
default[:bootstrap][:chef][:indexer_log] = "STDOUT"
else
default[:bootstrap][:chef][:client_log] = "#{bootstrap[:chef][:log_dir]}/client.log"
default[:bootstrap][:chef][:server_log] = "#{bootstrap[:chef][:log_dir]}/server.log"
default[:bootstrap][:chef][:indexer_log] = "#{bootstrap[:chef][:log_dir]}/indexer.log"
end
default[:bootstrap][:chef][:server_fqdn] = domain ? "chef.#{domain}" : "chef"
default[:bootstrap][:chef][:server_token] = validation_token
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/bootstrap/metadata.json
================================================
{
"description": "Configures RubyGems-installed Chef",
"replacing": {
},
"recipes": {
"bootstrap::client": "",
"bootstrap::server": "",
"bootstrap": ""
},
"platforms": {
},
"maintainer": "Opscode, Inc.",
"version": "0.1.0",
"recommendations": {
},
"name": "bootstrap",
"maintainer_email": "cookbooks@opscode.com",
"attributes": {
},
"suggestions": {
},
"license": "Apache 2.0",
"conflicting": {
},
"dependencies": {
"stompserver": [
],
"runit": [
],
"couchdb": [
],
"apache2": [
]
},
"providing": {
"bootstrap": [
],
"bootstrap::client": [
],
"bootstrap::server": [
]
},
"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-repo\/bootstrap.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"
}
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/bootstrap/metadata.rb
================================================
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.1"
%w{ runit couchdb stompserver apache2 }.each do |cb|
depends cb
end
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/bootstrap/recipes/client.rb
================================================
#
# 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"
)
# case node[:platform]
# when "debian", "ubuntu"
# package 'chef'
# require_recipe "ubuntu"
# else
# puts "not ubuntu"
# end
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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/bootstrap/recipes/default.rb
================================================
#
# 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.
#
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/bootstrap/recipes/server.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/bootstrap/templates/default/client.rb.erb
================================================
#
# 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] %>/chef-client.pid"
Chef::Log::Formatter.show_time = <%= @show_time %>
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/bootstrap/templates/default/server.rb.erb
================================================
#
# 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"
# 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 %>
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/bootstrap/templates/default/sv-chef-client-log-run.erb
================================================
#!/bin/sh
exec svlogd -tt ./main
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/bootstrap/templates/default/sv-chef-client-run.erb
================================================
#!/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 -%>
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/bootstrap/templates/default/sv-chef-indexer-log-run.erb
================================================
#!/bin/sh
exec svlogd -tt ./main
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/bootstrap/templates/default/sv-chef-indexer-run.erb
================================================
#!/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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/bootstrap/templates/default/sv-chef-server-log-run.erb
================================================
#!/bin/sh
exec svlogd -tt ./main
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/bootstrap/templates/default/sv-chef-server-run.erb
================================================
#!/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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/chef/README.rdoc
================================================
= 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.
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/chef/attributes/chef.rb
================================================
#
# 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)] }
default[:chef][:url_type] = "http"
default[:chef][:init_style] = "runit"
case platform
when "openbsd","freebsd"
default[:chef][:path] = "/var/chef"
default[:chef][:run_path] = "/var/run"
default[:chef][:cache_path] = "/var/chef/cache"
default[:chef][:serve_path] = "/var/chef"
else
default[:chef][:path] = "/srv/chef"
default[:chef][:run_path] = "/var/run/chef"
default[:chef][:cache_path] = "#{chef[:path]}/cache"
default[:chef][:serve_path] = "/srv/chef"
end
default[:chef][:server_version] = "0.7.10"
default[:chef][:client_version] = "0.7.10"
default[:chef][:client_interval] = "1800"
default[:chef][:client_splay] = "20"
default[:chef][:log_dir] = "/var/log/chef"
case chef[:init_style]
when "runit"
default[:chef][:client_log] = "STDOUT"
default[:chef][:indexer_log] = "STDOUT"
default[:chef][:server_log] = "STDOUT"
else
default[:chef][:client_log] = "#{chef[:log_dir]}/client.log"
default[:chef][:indexer_log] = "#{chef[:log_dir]}/indexer.log"
default[:chef][:server_log] = "#{chef[:log_dir]}/server.log"
end
default[:chef][:server_fqdn] = domain ? "chef.#{domain}" : "chef"
default[:chef][:server_token] = validation_token
default[:chef][:server_ssl_req] = "/C=US/ST=Several/L=Locality/O=Example/OU=Operations/" +
"CN=#{chef[:server_fqdn]}/emailAddress=ops@#{domain}"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/chef/metadata.json
================================================
{
"description": "Installs and configures chef client and server",
"replacing": {
},
"recipes": {
"chef::server": "Configures a chef-server as a passenger application",
"chef": "",
"chef::server_proxy": "",
"chef::client": "Sets up a client to talk to a chef-server"
},
"platforms": {
"ubuntu": [
],
"rhel": [
],
"centos": [
],
"debian": [
]
},
"maintainer": "Opscode, Inc.",
"version": "0.12.0",
"recommendations": {
},
"name": "chef",
"maintainer_email": "cookbooks@opscode.com",
"attributes": {
"chef\/server_ssl_req": {
"default": "\/C=US\/ST=Several\/L=Locality\/O=Example\/OU=Operations\/CN=chef_server_fqdn\/emailAddress=ops@domain",
"type": "string",
"multiple_values": false,
"description": "Data to pass for creating the SSL certificate",
"display_name": "Chef Server SSL Request",
"required": false,
"recipes": [
]
},
"chef\/server_path": {
"default": "gem_dir\/gems\/chef-server-chef_server_version",
"type": "string",
"multiple_values": false,
"description": "Location of the Chef Server assets",
"display_name": "Chef Server Path",
"required": false,
"recipes": [
]
},
"chef\/run_path": {
"default": "\/var\/run\/chef",
"type": "string",
"multiple_values": false,
"description": "Filesystem location for Chef 'run' files",
"display_name": "Chef Run Path",
"required": false,
"recipes": [
]
},
"chef\/client_log": {
"default": "STDOUT",
"type": "string",
"multiple_values": false,
"description": "Location of the chef client log",
"display_name": "Chef Client Log",
"required": false,
"recipes": [
]
},
"chef\/path": {
"default": "\/srv\/chef",
"type": "string",
"multiple_values": false,
"description": "Filesystem location for Chef files",
"display_name": "Chef Path",
"required": false,
"recipes": [
]
},
"chef\/server_log": {
"default": "\/var\/log\/chef\/server.log",
"type": "string",
"multiple_values": false,
"description": "Location of the Chef server log",
"display_name": "Chef Server Log",
"required": false,
"recipes": [
]
},
"chef\/client_splay": {
"default": "20",
"type": "string",
"multiple_values": false,
"description": "Random number of seconds to add to interval",
"display_name": "Chef Client Splay ",
"required": false,
"recipes": [
]
},
"chef\/client_version": {
"default": "0.7.10",
"type": "string",
"multiple_values": false,
"description": "Set the version of the client gem to install",
"display_name": "Chef Client Version",
"required": false,
"recipes": [
]
},
"chef\/server_fqdn": {
"default": "hostname.domain",
"type": "string",
"multiple_values": false,
"description": "FQDN of the Chef server for Apache vhost and SSL certificate and clients",
"display_name": "Chef Server Fully Qualified Domain Name",
"required": false,
"recipes": [
]
},
"chef\/server_version": {
"default": "0.7.10",
"type": "string",
"multiple_values": false,
"description": "Set the version of the server and server-slice gems to install",
"display_name": "Chef Server Version",
"required": false,
"recipes": [
]
},
"chef\/indexer_log": {
"default": "\/var\/log\/chef\/indexer.log",
"type": "string",
"multiple_values": false,
"description": "Location of the chef-indexer log",
"display_name": "Chef Indexer Log ",
"required": false,
"recipes": [
]
},
"chef\/client_interval": {
"default": "1800",
"type": "string",
"multiple_values": false,
"description": "Poll chef client process to run on this interval in seconds",
"display_name": "Chef Client Interval ",
"required": false,
"recipes": [
]
},
"chef\/server_token": {
"default": "randomly generated",
"type": "string",
"multiple_values": false,
"description": "Value of the validation_token",
"display_name": "Chef Server Validation Token",
"required": false,
"recipes": [
]
},
"chef\/server_hostname": {
"default": "hostname",
"type": "string",
"multiple_values": false,
"description": "Hostname for the chef server, for building FQDN",
"display_name": "Chef Server Hostname",
"required": false,
"recipes": [
]
}
},
"suggestions": {
},
"license": "Apache 2.0",
"conflicting": {
},
"dependencies": {
"passenger_apache2": [
],
"stompserver": [
],
"packages": [
],
"runit": [
],
"couchdb": [
],
"apache2": [
]
},
"providing": {
"chef::server": [
],
"chef::server_proxy": [
],
"chef": [
],
"chef::client": [
]
},
"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"
}
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/chef/metadata.rb
================================================
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.12"
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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/chef/recipes/client.rb
================================================
#
# 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
directory File.dirname(node[:chef][:client_log]) do
action :create
end
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
if node[:chef][:server_token]
execute "Register client node with Chef Server" do
not_if { File.exists?("#{node[:chef][:path]}/cache/registration") }
command "/usr/bin/env chef-client -t #{node[:chef][:server_token]}"
end
else
execute "Register client node with Chef Server" do
command "/usr/bin/env chef-client -t \`cat /etc/chef/validation_token\`"
not_if { File.exists?("#{node[:chef][:path]}/cache/registration") }
only_if { File.exists?("/etc/chef/validation_token") }
end
end
execute "Remove the validation token" do
command "rm /etc/chef/validation_token"
only_if { File.exists?("/etc/chef/validation_token") }
end
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/chef/recipes/default.rb
================================================
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/chef/recipes/server.rb
================================================
#
# 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 "http://localhost:5984/chef/_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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/chef/recipes/server_proxy.rb
================================================
#
# 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"
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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/chef/templates/default/chef_server.conf.erb
================================================
ServerName <%= @params[:server_name] %>
ServerAlias <% @params[:server_aliases].each do |a| %><%= "#{a}" %> <% end %>
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 %>
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]
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/chef/templates/default/client.rb.erb
================================================
#
# 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] %>/chef-client.pid"
Chef::Log::Formatter.show_time = <%= @show_time %>
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/chef/templates/default/server.rb.erb
================================================
#
# 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"
# 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 %>
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ec2/attributes/ec2_metadata.rb
================================================
#
# Cookbook Name:: ec2
# Attribute File:: ec2_metadata.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.
#
require 'net/http'
def get_from_ec2(thing="/")
base_url = "http://169.254.169.254/latest/meta-data" + thing
url = URI.parse(base_url)
req = Net::HTTP::Get.new(url.path)
res = Net::HTTP.start(url.host, url.port) {|http|
http.request(req)
}
res.body
end
if @attribute["domain"] =~ /\.amazonaws.com$/ || @attribute["domain"] == "ec2.internal"
ec2 true
get_from_ec2.split("\n").each do |key|
if key =~ /\/$/
get_from_ec2("/#{key}").split("\n").each do |extra_key|
attr_name = "ec2-#{key}-#{extra_key}"
attr_name.gsub!("/", "")
@attribute[attr_name] = get_from_ec2("/#{key}/#{extra_key}")
end
end
@attribute["ec2-#{key}"] = get_from_ec2("/#{key}")
end
end
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ec2/attributes/ec2_recipe_options.rb
================================================
#
# 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.
#
ec2opts Mash.new unless attribute?("ec2opts")
ec2opts[:lvm] = Mash.new unless ec2opts.has_key?(:lvm)
ec2opts[:lvm][:use_ephemeral] = true unless ec2opts[:lvm].has_key?(:use_ephemeral)
ec2opts[:lvm][:ephemeral_mountpoint] = "/mnt" unless ec2opts[:lvm].has_key?(:ephemeral_mountpoint)
ec2opts[:lvm][:ephemeral_volume_group] = "ephemeral" unless ec2opts[:lvm].has_key?(:ephemeral_volume_group)
ec2opts[:lvm][:ephemeral_logical_volume] = "store" unless ec2opts[:lvm].has_key?(:ephemeral_logical_volume)
ec2opts[:lvm][:ephemeral_devices] = {
"m1.small" => [ "/dev/sda2" ],
"m1.large" => [ "/dev/sdb", "/dev/sdc" ],
"m1.xlarge" => [ "/dev/sdb", "/dev/sdc", "/dev/sdd", "/dev/sde" ],
} unless ec2opts[:lvm].has_key?(:ephemeral_devices)
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ec2/metadata.rb
================================================
maintainer "Opscode, Inc."
maintainer_email "cookbooks@opscode.com"
license "Apache 2.0"
description "Manage EC2 metadata as attributes"
version "0.9"
attribute "ec2_metadata",
:display_name => "EC2 Metadata",
:description => "Retrieve EC2 instance metadata"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ec2/recipes/default.rb
================================================
#
# 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.
#
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ganglia/README.rdoc
================================================
= DESCRIPTION:
= REQUIREMENTS:
= ATTRIBUTES:
= USAGE:
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ganglia/attributes/default.rb
================================================
set[:ganglia][:ganglia_this_nodes_private_ip] = ipaddress
# set[:ganglia][:ganglia_gmetad_data_sources] = "foo"
# set[:ganglia][:ganglia_pool_name] = "name"
# set[:ganglia][:ganglia_cloud_name] = "foo"
# set[:ganglia][:ganglia_first_node_in_clusters_ip] = "ip"
# set[:ganglia][:gmond_user] = "root"
# set[:ganglia][:gmetad_user] = "root"
# set[:ganglia][:gmond_web_root] = "/var/www/ganglia"
# set[:ganglia][:ganglia_gmetad_data_sources] = %Q{data_source "a name goes here todo" 127.0.0.1} # space separated ip/hostnames
# # hopefully we can delete the attributes below now that the recipes are split up
# set[:ganglia][:enable_web_interface] = false
# set[:ganglia][:enable_gmetad] = false
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ganglia/metadata.rb
================================================
maintainer "Nate Murray"
maintainer_email ""
license "Apache 2.0"
description "Installs/Configures ganglia"
long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc'))
version "0.1"
recipe "ganglia::default"
recipe "ganglia::gmetad"
recipe "ganglia::monitor_sshd"
recipe "ganglia::monitor_watson"
recipe "ganglia::web"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ganglia/recipes/default.rb
================================================
#
# Cookbook Name:: ganglia
# Recipe:: default
# install rrd
case node[:platform]
when "redhat","centos"
package "apr-util-devel"
%w{cairo-devel libxml2-devel pango-devel pango libpng-devel freetype freetype-devel libart_lgpl-devel gettext}.each {|p| package p}
package "xorg-x11-fonts-Type1"
# crazy hack to get it to work b/c of conflicts with centos 64/32 bit. see comment #16 http://www.cyberciti.biz/faq/howto-install-rrdtool-on-rhel-linux/
script "64bit centos hack" do
interpreter "sh -x"
code <<-EOH
(( uname -a | grep x86_64 ))
if [ "$?" -eq "0" ]
then
yum remove -y libxml2-devel.i386 libcairo-devel.i386 libpng12-devel.i386 libpng-devel.i386 glib2-devel.i386
yum install -y pango-devel.x86_64
fi
# for some reason chef isn't installing these properly?
yum install -y "freetype-devel"
yum install -y "libpng-devel"
EOH
end
script "install rrdtool" do
interpreter "sh -x"
code <<-EOH
mkdir -p /opt/local/src
cd /opt/local/src
wget http://oss.oetiker.ch/rrdtool/pub/rrdtool-1.3.1.tar.gz -O rrdtool-1.3.1.tar.gz
tar -zxvf rrdtool-1.3.1.tar.gz
cd rrdtool-1.3.1
export PKG_CONFIG_PATH=/usr/lib/pkgconfig/
./configure && make && make install
EOH
creates "/usr/local/rrdtool-1.3.1/bin/rrdtool"
end
%w{rrdtool rrdcgi}.each do |r|
link "/usr/bin/#{r}" do
to "/usr/local/rrdtool-1.3.1/bin/#{r}"
end
end
script "install libconfuse" do
interpreter "sh -x"
code <<-EOH
mkdir -p /opt/local/src
cd /opt/local/src
wget http://bzero.se/confuse/confuse-2.6.tar.gz -O confuse-2.6.tar.gz
tar -xvvf confuse-2.6.tar.gz
cd confuse-2.6
./configure CFLAGS=-fPIC --disable-nls # tricksy - http://www.mail-archive.com/ganglia-general@lists.sourceforge.net/msg04731.html
make && make install
EOH
creates "/usr/local/lib/libconfuse.a"
end
script "install start-stop-daemon" do
interpreter "sh -x"
code <<-EOH
mkdir -p /opt/local/src
cd /opt/local/src
wget http://ftp.de.debian.org/debian/pool/main/d/dpkg/dpkg_1.14.25.tar.gz -O dpkg_1.14.25.tar.gz
tar -xf dpkg_1.14.25.tar.gz
cd dpkg-1.14.25
./configure --without-selinux --prefix=/usr
make
cd utils
make
make install
EOH
creates "/usr/sbin/start-stop-daemon"
end
when "ubuntu"
case node[:version]
when "8.04" # need to add jaunty sources to 8.04
execute "apt-get-update" do
action :nothing
command "apt-get update"
end
template "/etc/apt/sources.list.d/jaunty.list" do
source "jaunty.sources.list"
mode "0755"
notifies :restart, resources(:execute => "apt-get-update"), :immediately
end
end
%w{rrdtool build-essential librrd-dev libapr1-dev libconfuse-dev libexpat1-dev python-dev}.each {|p| package p}
end
script "install ganglia" do
interpreter "sh -x"
configure = "./configure --disable-python --with-librrd=/usr/local/rrdtool-1.3.1"
code <<-EOH
mkdir -p /opt/local/src
cd /opt/local/src
wget http://superb-west.dl.sourceforge.net/sourceforge/ganglia/ganglia-3.1.2.tar.gz -O ganglia.tar.gz
tar -xvvf ganglia.tar.gz
cd ganglia-3.1.2/
#{configure}
make && make install
EOH
creates "/usr/sbin/gmond"
notifies(:restart, resources(:service => "apache2")) rescue nil
end
# == configure ganglia
directory "/etc/ganglia/bin/monitors" do
action :create
recursive true
end
template "/etc/init.d/gmond" do
mode "0755"
source "bin/gmond.erb"
end
service "gmond" do
action :nothing
end
template "/etc/ganglia/gmond.conf" do
source "gmond.conf.erb"
mode "0755"
notifies :restart, resources(:service => "gmond")
end
# sudoers
# /usr/sbin/adduser ves
# ves ALL=(ALL) NOPASSWD: ALL
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ganglia/recipes/gmetad.rb
================================================
# Cookbook Name:: ganglia
# Recipe:: gmetad
template "/etc/init.d/gmetad" do
mode "0755"
source "bin/gmetad.erb"
end
service "gmetad" do
action :nothing
end
template "/etc/ganglia/gmetad.conf" do
source "gmetad.conf.erb"
mode "0755"
notifies :restart, resources(:service => "gmetad")
end
directory "/var/lib/ganglia/rrds" do
action :create
recursive true
owner node[:ganglia][:gmetad_user]
group node[:ganglia][:gmetad_user]
end
script "install ganglia" do
interpreter "sh -x"
configure = "./configure --with-gmetad --disable-python --with-librrd=/usr/local/rrdtool-1.3.1"
code <<-EOH
mkdir -p /opt/local/src
cd /opt/local/src
wget http://superb-west.dl.sourceforge.net/sourceforge/ganglia/ganglia-3.1.2.tar.gz -O ganglia.tar.gz
tar -xvvf ganglia.tar.gz
cd ganglia-3.1.2/
#{configure}
make && make install
EOH
creates "/usr/sbin/gmetad"
notifies(:restart, resources(:service => "apache2")) rescue nil
notifies :restart, resources(:service => "gmetad")
end
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ganglia/recipes/monitor_sshd.rb
================================================
template "/etc/ganglia/bin/monitors/sshd_ganglia.sh" do
source "bin/monitors/sshd_ganglia.sh.erb"
mode "0755"
end
cron "monitor sshd" do
minute "*/5"
hour "*"
day "*"
month "*"
weekday "*"
command "bash -c /etc/ganglia/bin/monitors/sshd_ganglia.sh"
end
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ganglia/recipes/monitor_watson.rb
================================================
template "/etc/ganglia/bin/monitors/watson_channels.sh" do
source "bin/monitors/watson_channels.sh.erb"
mode "0755"
end
cron "watson-channels" do
minute "*/1"
hour "*"
day "*"
month "*"
weekday "*"
command "bash -c /etc/ganglia/bin/monitors/watson_channels.sh"
end
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ganglia/recipes/web.rb
================================================
# Cookbook Name:: ganglia
# Recipe:: web
# hmm, maybe unify with below
execute "create ganglia web interface" do
command "cp -r /opt/local/src/ganglia-3.1.2/web #{node[:ganglia][:gmond_web_root]}"
action :run
creates node[:ganglia][:gmond_web_root]
end
# setenforce 0
execute "selinux: allow httpd can network for ganglia web interface" do
command "setsebool -P httpd_can_network_connect 1"
action :run
only_if "/usr/sbin/getenforce | grep Enforcing"
not_if "getsebool httpd_can_network_connect | grep on"
end
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ganglia/templates/default/bin/gmetad.erb
================================================
#!/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/sbin/gmetad
NAME=gmetad
DESC="Ganglia Monitor Meta-Daemon"
test -x $DAEMON || exit 0
set -e
case "$1" in
start)
echo -n "Starting $DESC: "
start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \
--exec $DAEMON
echo "$NAME."
;;
stop)
echo -n "Stopping $DESC: "
start-stop-daemon --stop --quiet --oknodo \
--exec $DAEMON 2>&1 > /dev/null
echo "$NAME."
;;
reload)
;;
restart|force-reload)
$0 stop
$0 start
;;
*)
N=/etc/init.d/$NAME
# echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
echo "Usage: $N {start|stop|restart|force-reload}" >&2
exit 1
;;
esac
exit 0
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ganglia/templates/default/bin/gmond.erb
================================================
#!/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/sbin/gmond
NAME=gmond
DESC="Ganglia Monitor Daemon"
test -x $DAEMON || exit 0
set -e
case "$1" in
start)
echo -n "Starting $DESC: "
start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \
--exec $DAEMON
echo "$NAME."
;;
stop)
echo -n "Stopping $DESC: "
start-stop-daemon --stop --quiet --oknodo \
--exec $DAEMON 2>&1 > /dev/null
echo "$NAME."
;;
reload)
;;
restart|force-reload)
$0 stop
$0 start
;;
*)
N=/etc/init.d/$NAME
# echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
echo "Usage: $N {start|stop|restart|force-reload}" >&2
exit 1
;;
esac
exit 0
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ganglia/templates/default/bin/monitors/sshd_ganglia.sh.erb
================================================
#!/bin/bash
# Nate Murray
# Date 2009-07-13
#
# Record number of invalid users or failed passwords in sshd
# Also check if this is the right location of gmetric
GMETRIC=/usr/bin/gmetric
AUTH_LOG=/var/log/auth
COUNT=$(cat $AUTH_LOG | grep -iE "(Invalid user|Failed password)" | grep "`date '+%b %e'`" | wc -l)
$GMETRIC --name "failed-or-invalid-login-attempts" --value $COUNT --type uint8 --units ''
exit 0
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ganglia/templates/default/bin/monitors/watson_channels.sh.erb
================================================
#!/bin/bash
# 2009-10-01
# Also check if this is the right location of gmetric
GMETRIC=/usr/bin/gmetric
AUTH_LOG=/var/log/auth
dEFAULT_COUNT=0
COUNT=$(echo "status" | nc localhost 8081 | grep free |awk '{print $2}')
[ "x" = "x$COUNT" ] && COUNT=${DEFAULT_COUNT}
$GMETRIC --name "watson-free-channels" --value $COUNT --type uint8 --units ''
exit 0
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ganglia/templates/default/ganglia-web-conf.php.erb
================================================
"ff634f",
"75-100" =>"ffa15e",
"50-75" => "ffde5e",
"25-50" => "caff98",
"0-25" => "e2ecff",
"down" => "515151"
);
#
# Load scaling
#
$load_scale = 1.0;
#
# Default color for single metric graphs
#
$default_metric_color = "555555";
#
# Default metric
#
$default_metric = "load_one";
#
# remove the domainname from the FQDN hostnames in graphs
# (to help with long hostnames in small charts)
#
$strip_domainname = false;
#
# Optional summary graphs
#
$optional_graphs = array('packet');
#
# Time ranges
# Each value is the # of seconds in that range.
#
$time_ranges = array(
'hour'=>3600,
'day'=>86400,
'week'=>604800,
'month'=>2419200,
'year'=>31449600
);
# this key must exist in $time_ranges
$default_time_range = 'hour';
#
# Graph sizes
#
$graph_sizes = array(
'small'=>array(
'height'=>40,
'width'=>130,
'fudge_0'=>0,
'fudge_1'=>0,
'fudge_2'=>0
),
'medium'=>array(
'height'=>75,
'width'=>300,
'fudge_0'=>0,
'fudge_1'=>14,
'fudge_2'=>28
),
'large'=>array(
'height'=>600,
'width'=>800,
'fudge_0'=>0,
'fudge_1'=>0,
'fudge_2'=>0
),
# this was the default value when no other size was provided.
'default'=>array(
'height'=>100,
'width'=>400,
'fudge_0'=>0,
'fudge_1'=>0,
'fudge_2'=>0
)
);
$default_graph_size = 'default';
$graph_sizes_keys = array_keys( $graph_sizes );
?>
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ganglia/templates/default/gmetad.conf.erb
================================================
# 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
# data_source "my cluster" localhost
#
<%= @node[:ganglia][:ganglia_gmetad_data_sources] %>
#
#
# 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][:ganglia_pool_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 <%= @node[:ganglia][:gmetad_user] %>
#
#-------------------------------------------------------------------------------
# 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 "/some/other/place"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ganglia/templates/default/gmond.conf.erb
================================================
/* 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 */
/* some comments borrowed from http://www-user.tu-chemnitz.de/~aas/wiki/lib/exe/fetch.php?id=projekte%3Aganglia&cache=cache&media=gmond.conf.template */
globals {
daemonize = yes
setuid = yes
user = <%= @node[:ganglia][:gmond_user] %>
debug_level = 0
max_udp_msg_len = 1472
mute = no
deaf = no
allow_extra_data = yes
host_dmax = 0 /*secs */
cleanup_threshold = 300 /*secs */
gexec = no
send_metadata_interval = 10
}
/*
* The cluster attributes specified will be used as part of the
* tag that will wrap all hosts collected by this instance.
*/
cluster {
name = "<%= @node[:ganglia][:ganglia_cloud_name] %>"
owner = "unspecified"
latlong = "unspecified"
url = "unspecified"
}
/* The host section describes attributes of the host, like the location */
host {
location = "unspecified"
}
/*
UDP send channels
Gmond will use UDP send channel(s) for sending monitoring
data it collects from _the local node_ to other nodes.
There are two different kinds of UDP send channels:
- unicast channel:
specifies a single host that is supposed to receive monitoring
data from other client nodes
- multicast channel:
specifies a multicast domain that will be used to spread monitoring data
Note:
- the type of channel that should be used depends on the kind of monitoring
architecture you want to implement
- make sure to set 'mute = no' in the global configuration section!
- multiple UDP send channels can be configured
- gmond will send its data through all channels simultaneously
See "Monitoringsoftware Ganglia - Installation, Konfiguration und Bewertung",
section "Architektur der Monitoringsoftware Ganglia" for further details!
Unicast UDP send channels:
- the target host has to be a node running gmond
- the gmond running on the target host has to be configured to
accept data from other gmonds
*/
/* 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 = <%= @node[:ganglia][:ganglia_masters_ip] %> */
/* host = <%= @node[:ganglia][:ganglia_masters_ip] %> */
host = <%= @node[:ganglia][:ganglia_first_node_in_clusters_ip] %>
port = 8649
ttl = 1
}
/*
UDP receive channels
Gmond will 'listen' on all UDP receive channels for incoming
monitoring data. Incoming data from other gmonds will be added
to the cluster state maintained by the local node.
There are two different kinds of UDP receive channels:
- unicast channel:
specifies a single UDP port that will accept incoming data
- multicast channel:
tells gmond to join a multicast domain and accept incoming
data sent by other members of the same domain
Note:
- the type of channel that should be used depends on the kind of monitoring
architecture you want to implement
- make sure to set 'deaf = no' in the global configuration section!
- multiple UDP receive channels can be configured
- gmond will accept data on all channels simultaneously
See "Monitoringsoftware Ganglia - Installation, Konfiguration und Bewertung",
section "Architektur der Monitoringsoftware Ganglia" for further details!
You can specify as many udp_recv_channels as you like. */
udp_recv_channel {
/* mcast_join = <%= @node[:ganglia][:ganglia_this_nodes_private_ip] %> */
port = 8649
bind = <%= @node[:ganglia][:ganglia_this_nodes_private_ip] %>
/* bind = 0.0.0.0 */
family = inet4
acl {
default = "allow"
}
}
<%# if @node[:ganglia][:ganglia_gmond_is_master] %>
<%# end %>
/*
TCP accept channels
Gmond will listen on all TCP accept channels for incoming connections
from nodes running gmetad. When a gmetad connects, gmond will transmit
_all monitoring data_ it has collected so far (i.e. the state of the
_whole cluster_ as far as gmond knows about it).
Note:
- make sure to set 'mute = no' in the global configuration section
- multiple TCP accept channels can be configured
- gmond will accept requests on all channels simultaneously
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
bind = <%= @node[:ganglia][:ganglia_this_nodes_private_ip] %>
acl {
default = "allow"
}
}
/* 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 = "modcpu.so"
}
module {
name = "disk_module"
path = "moddisk.so"
}
module {
name = "load_module"
path = "modload.so"
}
module {
name = "mem_module"
path = "modmem.so"
}
module {
name = "net_module"
path = "modnet.so"
}
module {
name = "proc_module"
path = "modproc.so"
}
module {
name = "sys_module"
path = "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"
}
}
/* vim: ft=javascript */
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ganglia/templates/default/jaunty.sources.list.erb
================================================
deb http://archive.ubuntu.com/ubuntu/ jaunty main restricted
deb http://archive.ubuntu.com/ubuntu/ jaunty universe
deb http://archive.ubuntu.com/ubuntu/ jaunty multiverse
deb-src http://archive.ubuntu.com/ubuntu/ jaunty main restricted
deb-src http://archive.ubuntu.com/ubuntu/ jaunty universe
deb-src http://archive.ubuntu.com/ubuntu/ jaunty multiverse
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ntp/attributes/ntp.rb
================================================
ntp Mash.new unless attribute?("ntp")
case platform
when "ubuntu","debian"
ntp[:service] = "ntp"
when "redhat","centos","fedora"
ntp[:service] = "ntpd"
end unless ntp.has_key?(:service)
ntp[:is_server] = false unless ntp.has_key?(:is_server)
ntp[:servers] = ["0.us.pool.ntp.org", "1.us.pool.ntp.org"] unless ntp.has_key?(:servers)
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ntp/metadata.json
================================================
{
"description": "Installs and configures ntp as a client or server",
"replacing": {
},
"recipes": {
"ntp": ""
},
"platforms": {
"ubuntu": [
],
"fedora": [
],
"centos": [
],
"redhat": [
],
"debian": [
]
},
"maintainer": "Opscode, Inc.",
"version": "0.7.0",
"recommendations": {
},
"name": "ntp",
"maintainer_email": "cookbooks@opscode.com",
"attributes": {
"ntp\/servers": {
"default": [
"0.us.pool.ntp.org",
"1.us.pool.ntp.org"
],
"type": "array",
"multiple_values": false,
"description": "Array of servers we should talk to",
"display_name": "NTP Servers",
"required": false,
"recipes": [
]
},
"ntp\/is_server": {
"default": "false",
"type": "string",
"multiple_values": false,
"description": "Set to true if this is an NTP server",
"display_name": "NTP Is Server?",
"required": false,
"recipes": [
]
},
"ntp\/service": {
"default": "ntp",
"type": "string",
"multiple_values": false,
"description": "Name of the NTP service",
"display_name": "NTP Service",
"required": false,
"recipes": [
]
},
"ntp": {
"type": "hash",
"multiple_values": false,
"description": "Hash of NTP attributes",
"display_name": "NTP",
"required": false,
"recipes": [
]
}
},
"suggestions": {
},
"license": "Apache 2.0",
"conflicting": {
},
"dependencies": {
},
"providing": {
"ntp": [
]
},
"long_description": ""
}
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ntp/metadata.rb
================================================
maintainer "Opscode, Inc."
maintainer_email "cookbooks@opscode.com"
license "Apache 2.0"
description "Installs and configures ntp as a client or server"
version "0.7"
%w{ ubuntu debian redhat centos fedora }.each do |os|
supports os
end
attribute "ntp",
:display_name => "NTP",
:description => "Hash of NTP attributes",
:type => "hash"
attribute "ntp/service",
:display_name => "NTP Service",
:description => "Name of the NTP service",
:default => "ntp"
attribute "ntp/is_server",
:display_name => "NTP Is Server?",
:description => "Set to true if this is an NTP server",
:default => "false"
attribute "ntp/servers",
:display_name => "NTP Servers",
:description => "Array of servers we should talk to",
:type => "array",
:default => ["0.us.pool.ntp.org", "1.us.pool.ntp.org"]
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ntp/recipes/default.rb
================================================
#
# Cookbook Name:: ntp
# 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.
case node[:platform]
when "ubuntu","debian"
package "ntpdate" do
action :install
end
end
package "ntp" do
action :install
end
service node[:ntp][:service] do
action :start
end
template "/etc/ntp.conf" do
source "ntp.conf.erb"
owner "root"
group "root"
mode 0644
notifies :restart, resources(:service => node[:ntp][:service])
end
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/ntp/templates/default/ntp.conf.erb
================================================
driftfile /var/lib/ntp/ntp.drift
statsdir /var/log/ntpstats/
statistics loopstats peerstats clockstats
filegen loopstats file loopstats type day enable
filegen peerstats file peerstats type day enable
filegen clockstats file clockstats type day enable
<% if @node[:ntp][:is_server] -%>
server 0.us.pool.ntp.org
server 1.us.pool.ntp.org
server 2.us.pool.ntp.org
server 3.us.pool.ntp.org
<% else -%>
<% @node[:ntp][:servers].each do |ntpserver| -%>
server <%= ntpserver %>
<% end -%>
<% end -%>
restrict default kod notrap nomodify nopeer noquery
restrict 127.0.0.1 nomodify
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/rsyslog/README.rdoc
================================================
= 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.
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/rsyslog/attributes/rsyslog.rb
================================================
#
# 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.
#
rsyslog Mash.new unless attribute?("rsyslog")
rsyslog[:log_dir] = "/srv/rsyslog" unless rsyslog.has_key?(:log_dir)
rsyslog[:server] = false unless rsyslog.has_key?(:server)
rsyslog[:protocol] = "tcp" unless rsyslog.has_key?(:protocol)
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/rsyslog/files/default/rsyslog.default
================================================
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/rsyslog/files/ubuntu-8.04/rsyslog.default
================================================
# Options to rsyslogd
# -m 0 disables 'MARK' messages.
# -r enables logging from remote machines
# -x disables DNS lookups on messages recieved with -r
# See rsyslogd(8) for more details
RSYSLOGD_OPTIONS="-m 0"
# Options to rklogd
# -2 prints all kernel oops messages twice; once for klogd to decode, and
# once for processing with 'ksymoops'
# -x disables all klogd processing of oops messages entirely
# See rklogd(8) for more details
RKLOGD_OPTIONS="-x"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/rsyslog/files/ubuntu-9.10/rsyslog.default
================================================
# 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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/rsyslog/metadata.json
================================================
{
"description": "Installs and configures rsyslog",
"replacing": {
},
"recipes": {
"rsyslog::server": "Sets up an rsyslog server",
"rsyslog": "",
"rsyslog::client": "Sets up a client to log to a remote rsyslog server"
},
"platforms": {
},
"maintainer": "Opscode, Inc.",
"version": "0.7.0",
"recommendations": {
},
"name": "rsyslog",
"maintainer_email": "cookbooks@opscode.com",
"attributes": {
"rsyslog\/protocol": {
"default": "tcp",
"type": "string",
"multiple_values": false,
"description": "Set which network protocol to use for rsyslog",
"display_name": "Rsyslog Protocol",
"required": false,
"recipes": [
]
},
"rsyslog": {
"type": "hash",
"multiple_values": false,
"description": "Hash of Rsyslog attributes",
"display_name": "Rsyslog",
"required": false,
"recipes": [
]
},
"rsyslog\/log_dir": {
"default": "\/srv\/rsyslog",
"type": "string",
"multiple_values": false,
"description": "Filesystem location of logs from clients",
"display_name": "Rsyslog Log Directory",
"required": false,
"recipes": [
]
},
"rsyslog\/server": {
"default": "false",
"type": "string",
"multiple_values": false,
"description": "Is this node an rsyslog server?",
"display_name": "Rsyslog Server?",
"required": false,
"recipes": [
]
}
},
"suggestions": {
},
"license": "Apache 2.0",
"conflicting": {
},
"dependencies": {
},
"providing": {
"rsyslog::server": [
],
"rsyslog": [
],
"rsyslog::client": [
]
},
"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"
}
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/rsyslog/metadata.rb
================================================
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"
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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/rsyslog/recipes/client.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/rsyslog/recipes/default.rb
================================================
#
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/rsyslog/recipes/server.rb
================================================
#
# 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 "/srv/rsyslog" 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
notifies :reload, resources(:service => "rsyslog"), :delayed
only_if do File.exists?("/etc/rsyslog.d/remote.conf") end
end
cron "rsyslog_gz" do
minute "0"
hour "4"
command "find #{node[:rsyslog][:log_dir]}/$(date +\\%Y) -type f -mtime +1 -exec gzip -q {} \\;"
end
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/rsyslog/templates/default/remote.conf.erb
================================================
<% case @protocol -%>
<% when "tcp" -%>
*.* @@<%= @server %>
<% when "udp" -%>
*.* @<%= @server %>
<% end -%>
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/rsyslog/templates/default/rsyslog.conf.erb
================================================
# /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
###############
#### 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
#
# Include all config files in /etc/rsyslog.d/
#
$IncludeConfig /etc/rsyslog.d/*.conf
<% case @protocol -%>
<% when "tcp" -%>
*.* @@<%= @server %>
<% when "udp" -%>
*.* @<%= @server %>
<% end -%>
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/rsyslog/templates/default/server.conf.erb
================================================
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/rsyslog/templates/ubuntu-9.10/50-default.conf.erb
================================================
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/rsyslog/templates/ubuntu-9.10/remote.conf.erb
================================================
<% case @protocol -%>
<% when "tcp" -%>
*.* @@<%= @server %>
<% when "udp" -%>
*.* @<%= @server %>
<% end -%>
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/rsyslog/templates/ubuntu-9.10/server.conf.erb
================================================
# 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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/runit/attributes/sv_bin.rb
================================================
#
# Cookbook Name:: runit
# Attribute File:: sv_bin
#
# Copyright 2008, 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.
#
runit Mash.new
case platform
when "ubuntu","debian"
runit[:sv_bin] = "/usr/bin/sv"
runit[:service_dir] = "/etc/service"
runit[:sv_dir] = "/etc/sv"
end
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/runit/definitions/runit_service.rb
================================================
#
# Cookbook Name:: runit
# Definition:: runit_service
#
# Copyright 2008, 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
template "#{sv_dir_name}/run" do
mode 0755
source "sv-#{params[:name]}-run.erb"
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[:name]}-log-run.erb"
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
service params[:name] do
supports :restart => true, :status => true
action :nothing
end
#execute "#{params[:name]}-down" do
# command "/etc/init.d/#{params[:name]} down"
# only_if do params[:only_if] end
#end
end
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/runit/files/ubuntu-6.10/runsvdir
================================================
start on runlevel-2
start on runlevel-3
start on runlevel-4
start on runlevel-5
stop on shutdown
respawn /usr/sbin/runsvdir-start
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/runit/files/ubuntu-7.04/runsvdir
================================================
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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/runit/files/ubuntu-7.10/runsvdir
================================================
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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/runit/files/ubuntu-8.04/runsvdir
================================================
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
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/runit/metadata.json
================================================
{
"maintainer": "Opscode, Inc.",
"description": "Installs runit and provides runit_service definition",
"recommendations": {
},
"maintainer_email": "cookbooks@opscode.com",
"suggestions": {
},
"platforms": {
"ubuntu": [
],
"debian": [
]
},
"version": "0.7.0",
"recipes": {
"runit": ""
},
"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": {
}
}
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/runit/metadata.rb
================================================
maintainer "Opscode, Inc."
maintainer_email "cookbooks@opscode.com"
license "Apache 2.0"
description "Installs runit and provides runit_service definition"
version "0.7"
%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"
================================================
FILE: examples/chef_cloud/chef_repo/cookbooks/runit/recipes/default.rb
================================================
#
# Cookbook Name:: runit
# Recipe:: default
#
# Copyright 2008, 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
================================================
FILE: examples/chef_cloud/chef_repo/roles/README
================================================
Create roles here, in either .rb or .json files. Any roles created here win vs those created in the webui.
================================================
FILE: examples/chef_cloud/chef_repo/roles/chef-upgrade.rb
================================================
name "chef-upgrade"
description "Use this role to upgrade Chef clients and server with the Opscode Chef cookbook. Requires that nodes are using chef::client or chef::server recipes."
# Update the version number to upgrade Chef to a newer version.
# You may need to add an override attribute under 'chef' for 'server_path'
# depending on your installation, eg:
# "server_path" => "/usr/lib/ruby/gems/1.8/gems/chef-server-0.7.10"
# On the line after server_version. Don't forget the comma ;).
override_attributes(
"chef" => {
"client_version"=>"0.7.10",
"server_version"=>"0.7.10"
}
)
================================================
FILE: examples/chef_cloud/chef_repo/site-cookbooks/README
================================================
This directory contains cookbooks that modify upstream ones, or that are specific to your site.
================================================
FILE: examples/chef_cloud/chef_repo/site-cookbooks/hosts/attributes/hosts.rb
================================================
hosts Mash.new unless attribute?(:hosts)
================================================
FILE: examples/chef_cloud/chef_repo/site-cookbooks/hosts/metadata.rb
================================================
maintainer "37signals"
maintainer_email "sysadmins@37signals.com"
description "Configures hosts"
version "0.1"
================================================
FILE: examples/chef_cloud/chef_repo/site-cookbooks/hosts/recipes/default.rb
================================================
template "/etc/hosts" do
source "hosts.erb"
end
================================================
FILE: examples/chef_cloud/chef_repo/site-cookbooks/hosts/templates/default/hosts.erb
================================================
127.0.0.1 localhost <%= @node[:hosts][:localhost_aliases].join(" ") if @node[:hosts][:localhost_aliases] %>
127.0.1.1 <%= @node['fqdn'] %> <%= @node['hostname'] %>
<% if @node[:hosts][:entries] %>
<% @node[:hosts][:entries].each do |h| %>
<%= h.join(" ") %>
<% end %>
<% 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
================================================
FILE: examples/chef_cloud/chef_repo/site-cookbooks/sysadmin/recipes/default.rb
================================================
case node[:platform]
when "debian", "ubuntu"
# package "policykit"
# package "emacs22-nox"
require_recipe "apt"
else
# package "emacs-nox"
end
package "vim"
package "tree"
package "nmap"
package "ngrep"
package "irb"
package "curl"
package "man-db"
package "strace"
package "host"
package "lsof"
package "gdb"
package "socat"
package "procmail"
================================================
FILE: examples/chef_cloud/chef_repo/site-cookbooks/ubuntu/metadata.json
================================================
{
"description": "Sets up sources for ubuntu",
"replacing": {
},
"recipes": {
"ubuntu": ""
},
"platforms": {
"ubuntu": [
]
},
"maintainer": "Opscode, Inc.",
"version": "0.7.0",
"recommendations": {
},
"name": "ubuntu",
"maintainer_email": "cookbooks@opscode.com",
"attributes": {
},
"suggestions": {
},
"license": "Apache 2.0",
"conflicting": {
},
"dependencies": {
"apt": [
]
},
"providing": {
"ubuntu": [
]
},
"long_description": ""
}
================================================
FILE: examples/chef_cloud/chef_repo/site-cookbooks/ubuntu/metadata.rb
================================================
maintainer "Opscode, Inc."
maintainer_email "cookbooks@opscode.com"
license "Apache 2.0"
description "Sets up sources for ubuntu"
version "0.7"
depends "apt"
supports "ubuntu"
================================================
FILE: examples/chef_cloud/chef_repo/site-cookbooks/ubuntu/recipes/default.rb
================================================
#
# 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.
#
template "/etc/apt/sources.list" do
mode 0644
variables :code_name => node[:lsb][:codename]
source "sources.list.erb"
end
execute 'opscode apt key' do
command "cd /etc/apt/ && curl --output opscode.com.gpg.key http://apt.opscode.com/packages@opscode.com.gpg.key && sudo apt-key add opscode.com.gpg.key && apt-get update"
creates "/etc/apt/opscode.com.gpg.key"
end
#include_recipe "apt"
================================================
FILE: examples/chef_cloud/chef_repo/site-cookbooks/ubuntu/templates/default/sources.list.erb
================================================
#
# Ubuntu <%= @code_name %> - Generated by Chef
#
deb http://apt.opscode.com/ <%= @code_name %> universe
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
================================================
FILE: examples/chef_cloud/user_data
================================================
#!/bin/sh -x
echo "Yeah, go userdata script!"
================================================
FILE: examples/chef_cloud.rb
================================================
pool "poolparty" do
cloud "chef" do
instances 1
using :ec2
chef :solo do
repo File.dirname(__FILE__)+"/chef_cloud/chef_repo"
recipe "apache2"
recipe "rsyslog::server"
recipe "collectd"
attributes :apache2 => {:listen_ports => ["80", "8080"]}
end
user_data open(File.dirname(__FILE__)+"/chef_cloud/user_data").read
security_group do
authorize :from_port => "22", :to_port => "22"
authorize :from_port => "80", :to_port => "80"
end
end
end
================================================
FILE: examples/ec2_infrastructure_only.rb
================================================
require File.dirname(__FILE__)+'/../lib/poolparty.rb'
$PP_VERBOSE=true
pool "CloudteamExample" do
do_not_execute = true
#TODO: declarative, serializable syntax, and allow definition outside a cloud
# security_group 'minerva_monitor', [
# {:port=>80, :protocol=>'tcp', :network=>'0.0.0.0/0'},
# {:port=>443, :protocol=>'tcp', :network=>'0.0.0.0/0'},
# {:port=>22, :protocol=>'tcp', :network=>'0.0.0.0/0'},
# {:port=>25826, :protocol=>'udp', :security_group=>'minerva_chacha'}
# ]
# end
#TODO: allow definition of a load_balancer in the pool that balances between clouds, default name of pool.name
#TODO: default cloud load_balancer taking an array of balancer/port/protocol hashes.
#NOTE: loadbalancers are paid for per balancer, and are limited availabiliyt, so good to use multiple ports per LB
# load_balancer [ {:external_port=>80, :internal_port=>8080, :protocol=>'tcp'},
# {:external_port=>443, :internal_port=>8443, :protocol=>'tcp'} ]
cloud "basic" do
minimum_instances 1
maximum_instances 2
#keypair 'keyname' #keypair will be generated if it does not exist
using :ec2
security_group "chaca_thin_test_group" do
revoke :from_port => "8080", :to_port => "8081" #NOTE: why have a revoke method? closed by default, and only what is declared is open
authorize :from_port => "22", :to_port => "22"
end
availability_zones ['us-east-1b', 'us-east-1c']
load_balancer do
listener :external_port => 8080, :internal_port => 8080, :protocol => 'tcp'
end
autoscaler
end
end
#####################################################################################
# Testing Stuff #
#####################################################################################
# pool.run #uncomment to execute the cloud
sleep 4
puts "pool has been run\n----"
puts "describing current autscaling activities:"
# Set things up for amazon-ec2
@opts={:access_key_id => ENV['EC2_ACCESS_KEY'], :secret_access_key => ENV['EC2_SECRET_KEY'], :symbolize_keys=>:snake_case}
if ENV['EC2_URL']
@opts[:server] = URI.parse(ENV['EC2_URL']).host
end
@ec2 = AWS::EC2::Base.new(@opts)
@elb = AWS::ELB::Base.new(@opts)
@as = AWS::Autoscaling::Base.new(@opts)
# Iterate over the clods and ensure the elb and autoscaling groups exist
pool.clouds.each do |name,cld|
puts "\ncloud[:#{name}]\n"
cld.autoscalers.each do |asg|
puts "AS #{asg.first} ===========================================\n"
grp = @as.describe_autoscaling_groups['DescribeAutoScalingGroupsResult']['AutoScalingGroups']['member'].select_with_hash('AutoScalingGroupName'=>asg.first)
(!grp || grp.empty?) ? warn("ASGroup #{asg.first} was not created") : pp(grp)
activities = @as.describe_scaling_activities(:autoscaling_group_name=>'cloudteam-cha')["DescribeScalingActivitiesResult"]["Activities"]["member"]
activities.each{|act| puts act.Cause}
end
cld.load_balancers.each do |name, lb|
puts "ELB #{name} ===========================================\n"
elbt = @elb.describe_load_balancers.DescribeLoadBalancersResult.LoadBalancerDescriptions.member.select_with_hash(:load_balancer_name=>name)
(!elbt || elbt.empty?) ? warn("LB #{name} was not created: #{elbt}") : pp(elbt)
end
end
================================================
FILE: examples/knock.sh
================================================
ok() {
exec 3>&1
eval `
exec 4>&1 >&3 3>&-
{
eval "$@" 2>&1 >/dev/null
echo "EC=$?;" >&4
} | sed 's/^/# /'
`
[ "$EC" = 0 ] && echo "ok - $@" || echo "not ok - $@: $EC"
return $EC
}
# example usage:
# ok true
# ok ! false
#
# ok "(ok true | grep -q '^ok')"
# ok "(ok false | grep -q '^not ok')"
#
# # self-test
# ok ok true
# ok ! ok "false"
#
# ok "(ok 'echo foo>/dev/stderr' 2>&1 | grep -q '# foo') # stderr gets commented"
================================================
FILE: examples/rds_cloud.rb
================================================
# example pool with RDS enabled
pool :poolparty do
cloud :app do
using :ec2
# this block will create an RDS DB instance
# by default, the instance id will match the containing cloud
# ("poolparty-app" in this example)
rds do
username "admin" # required
password "secret" # required
# ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
# these properties have the following overridable defaults:
#
# storage 5
# instance_class "db.m1.small"
# engine "db.m1.small"
# ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
# by default, a user DB will be created with a name that matches the containing cloud...
# ...except RDS does not allow hyphens in DB names, so it will be pool name *underscore* cloud name
# ("poolparty_app" in this example).
#
# to override this, use the database method, like so:
# database :db1
# - or -
# databases :production, :staging # :databases is aliased to :database for nice DSL reading. :)
# ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
# You are required to explicitly open access to the DB server to IP addresses (for outside EC2) or security groups (for inside EC2)
# access will be granted to your containing cloud's security groups unless you explicitly list security group authorization.
#
# to override, use the :authorize method, like so:
# authorize :networks => "1.2.3.4/24" # <--- will authorize access to this CIDR block *in addition to* your EC2 security group
# authorize :networks => ["1.2.3.4/32", "10.10.10.10/24"], :security_groups => "my_ec2_group"
end
end
end
================================================
FILE: examples/simple.rb
================================================
# the following three lines are only necessary if you want to irb -r thisfile
$:.unshift("#{File.dirname(__FILE__)}/../lib")
require "rubygems"
require "poolparty"
pool "poolparty" do
cloud "simple" do
instances 1..3
using :ec2
# autoscale do
# trigger :lower_threshold => 0.3, :upper_threshold => 1.0, :measure => :cpu
# end
image_id "ami-ccf615a5" #alestic jaunty
availability_zones ['us-east-1b']
#TODO: accept array of hashes defining security group rules
# security_gropup [
# {:port=>22, :protocol=>'tcp' },
# {:port=>80, :protocol=>'tcp' :source=>'10.0.0.0/8', :group=>'monitor'},
# {:port=>3000..3006, :protocol=>'tcp' :group=>'monitor' },
# {:port=>53, :protocol=>'udp' }
# ]
security_group "dummy-test-security-group" do
%w(22 80 443 8642).each {|port| authorize :from_port => port, :to_port => port}
end
# load_balancer do
# listener :external_port => 8080, :internal_port => 8080, :protocol => 'tcp'
# end
end
end
================================================
FILE: lib/cloud_providers/cloud_provider.rb
================================================
=begin rdoc
CloudProvider is the base class for cloud computing services such as Ec2, Eucalyptus - where your servers run.
=end
module CloudProviders
class CloudProvider
include Dslify
default_options(
:image_id => 'ami-ed46a784',
:user => "root",
:bootstrap_gems => []
)
attr_reader :name, :init_opts
def initialize(name, init_opts={}, &block)
@name = name
if name.is_a?(Hash) && init_opts.empty?
@init_opts = name
else
@init_opts = init_opts
end
set_vars_from_options(init_opts)
instance_eval &block if block
after_initialized
end
def after_initialized
end
def run
warn "#{self.class} does not implement run. Something is wrong"
end
def default_keypair_path
self.class.default_keypair_path
end
def self.default_keypair_path
ENV["EC2_CONFIG_DIR"] || "#{ENV["HOME"]}/.ssh"
end
def bootstrap_nodes!
end
def method_missing(m,*a,&block)
if cloud && cloud.respond_to?(m)
cloud.send(m,*a,&block)
else
super
end
end
private
def proper_name
cloud.proper_name
end
def cloud
init_opts.has_key?(:cloud) ? init_opts[:cloud] : nil
end
def maybe(action_description, default='Y', &block)
puts "About to #{action_description}. Type 'Y' to do this, 'N' to skip. #{default} will be chosen within 10 seconds."
begin
Timeout::timeout(10) do
line = $stdin.readline
end
rescue Timeout::Error => e
line = default
puts "Timeout: #{default} default will be used."
end
if line =~ /^Y/i
block.call
else
puts "Skipping."
end
end
end
end
================================================
FILE: lib/cloud_providers/connections.rb
================================================
require "open3"
module CloudProviders
module Connections
# hostname or ip to use when running remote commands
def host(n=nil)
if n.nil?
@host ||= dns_name
else
@host = n
end
end
def ping_port(host, port=22, retry_times=400)
connected = false
retry_times.times do |i|
begin
break if connected = TCPSocket.new(host, port).is_a?(TCPSocket)
rescue Exception => e
sleep(2)
end
end
connected
end
def run(commands, o={})
ssh(commands)
end
def shell_escape(str)
String(str).gsub(/(?=["'\\$])/n, '\\').
gsub(/\n/, "'\n'").
sub(/^$/, "''")
end
def ssh( commands=[], extra_ssh_ops={})
# commands can be single commands, as well as array
commands = [commands] unless commands.respond_to? :each
# Get the environment hash out of
# the extra_ssh_ops and then delete
# the element
ssh_error_msg="SSH is not available for this node. perhaps you need to authorize it?"
raise PoolParty::PoolPartyError.create("SSHError", ssh_error_msg) unless ssh_available?
env = extra_ssh_ops[:env] || {}
extra_ssh_ops.delete :env
# Decide to use sudo or not
do_sudo = user!="root"
if extra_ssh_ops.has_key? :do_sudo
do_sudo = extra_ssh_ops[:do_sudo]
extra_ssh_ops.delete :do_sudo
end
envstring = env.collect {|k,v| "#{k}=#{v}"}.join ' && '
envstring += " && " unless envstring.size == 0
ssh_string = "ssh #{user}@#{host} #{ssh_options(extra_ssh_ops)}"
if commands.empty?
#TODO: replace this with a IO.popen call with read_nonblocking to show progress, and accept input
Kernel.system(ssh_string)
else
r = nil
commands.each do |command|
cmd = "#{envstring}#{command}"
if do_sudo
sudocmd = %Q% sudo sh -c "#{shell_escape cmd}" %
else
sudocmd = cmd
end
r = system_run ssh_string + %Q% "#{shell_escape sudocmd}"%
end
r
end
end
# remove hostname and corresponding from known_hosts file. Avoids warning when reusing elastic_ip, and
# less likely, if amazone reassigns ip. By default removes both dns_name and ip
def ssh_cleanup_known_hosts!(hosts=[host, public_ip])
hosts = [hosts] unless hosts.respond_to? :each
hosts.compact.each do |name|
system_run "ssh-keygen -R %s" % name
end
end
# Take a hash of options and join them into a string, combined with default options.
# Default options are -o StrictHostKeyChecking=no -i keypair.full_filepath -l user
# {'-i'=>'keyfile, '-l' => 'fred' } would become
# "-i keyfile -o StrictHostKeyChecking=no -i keypair.to_s -l fred"
def ssh_options(opts={})
return @ssh_options if @ssh_options && opts.empty?
ssh_opts = {"-i" => keypair.full_filepath,
"-o" =>"StrictHostKeyChecking=no",
}.merge(opts)
@ssh_options = ssh_opts.collect{ |k,v| "#{k} #{v}"}.join(' ')
end
def rsync( opts={} )
raise StandardError.new("You must pass a :source=>uri option to rsync") unless opts[:source]
ssh_error_msg="SSH is not available for this node. perhaps you need to authorize it?"
raise PoolParty::PoolPartyError.create("SSHError", ssh_error_msg) unless ssh_available?
destination_path = opts[:destination] || opts[:source]
rsync_opts = opts[:rsync_opts] || '-va'
rsync_opts += %q% --rsync-path="sudo rsync"% unless user=="root"
rsync_opts += %q% --exclude=.svn --exclude=.git --exclude=.cvs %
cmd_string = "rsync -L -e 'ssh #{ssh_options}' #{rsync_opts} #{opts[:source]} #{user}@#{host}:#{destination_path}"
out = system_run(cmd_string, :quiet => true)
out
end
def scp(opts={})
source = opts[:source]
destination_path = opts[:destination] || opts[:source]
raise StandardError.new("You must pass a local_file to scp") unless source
scp_opts = opts[:scp_opts] || ""
cmd_string = "scp #{ssh_options(scp_opts)} #{source} #{user}@#{host}:#{destination_path}"
out = system_run(cmd_string)
out
end
private
# Execute command locally.
# This method is mainly broken out to ease testing in the other methods
# It opens the 3 IO outputs (stdin, stdout, stderr) and print the output out
# as the command runs, unless the quiet option is passed in
def system_run(cmd, o={})
opts = {:quiet => false, :sysread => 1024}.merge(o)
buf = ""
# puts("Running command: #{cmd}")
status = Open3.popen3(cmd) do |stdout, stdin, stderr, wait_thr|
begin
while (chunk = stdin.readpartial(opts[:sysread]))
buf << chunk
unless chunk.nil? || chunk.empty?
if not opts[:quiet]
$stdout.write(chunk) #if debugging? || verbose?
end
end
end
err = stderr.readlines
$stderr.write_nonblock(err)
rescue SystemCallError => error
err = stderr.readlines
$stderr.write_nonblock(err)
rescue EOFError => error
err = stderr.readlines
$stderr.write_nonblock(err)
# used to do nothing
end
wait_thr.value
end
unless status.success?
warn "Failed sshing. Check ~/.poolparty/ssh.log for details"
end
buf
end
end
end
================================================
FILE: lib/cloud_providers/default/base.rb
================================================
module CloudProviders
class Base < CloudProvider
end
end
================================================
FILE: lib/cloud_providers/default/helpers/base_helper.rb
================================================
================================================
FILE: lib/cloud_providers/ec2/ec2.rb
================================================
=begin rdoc
EC2 CloudProvider
This serves as the basis for running PoolParty on Amazon's ec2 cloud.
=end
module CloudProviders
class Ec2 < CloudProvider
# Set the aws keys from the environment, or load from /etc/poolparty/env.yml if the environment variable is not set
def self.default_access_key
ENV['EC2_ACCESS_KEY'] || load_keys_from_file[:access_key] || load_keys_from_credential_file[:access_key]
end
def self.default_secret_access_key
ENV['EC2_SECRET_KEY'] || load_keys_from_file[:secret_access_key] || load_keys_from_credential_file[:secret_access_key]
end
def self.default_private_key
ENV['EC2_PRIVATE_KEY'] || load_keys_from_file[:private_key]
end
def self.default_cert
ENV['EC2_CERT'] || load_keys_from_file[:cert]
end
def self.default_user_id
ENV['EC2_USER_ID'] || load_keys_from_file[:user_id]
end
def self.default_ec2_url
ENV['EC2_URL'] || load_keys_from_file[:ec2_url]
end
def self.default_s3_url
ENV['S3_URL'] || load_keys_from_file[:s3_url]
end
def self.default_cloud_cert
ENV['CLOUD_CERT'] || ENV['EUCALYPTUS_CERT'] || load_keys_from_file[:cloud_cert]
end
def self.default_credential_file
ENV['AWS_CREDENTIAL_FILE'] || load_keys_from_file[:credential_file]
end
# Load the yaml file containing keys. If the file does not exist, return an empty hash
def self.load_keys_from_file(filename=POOLPARTY_CONFIG_FILE, caching=true)
return @aws_yml if @aws_yml && caching==true
return {} unless File.exists?(filename)
puts("Reading keys from file: #{filename}")
@aws_yml = YAML::load( open(filename).read ) || {}
end
# Load credentials from file
def self.load_keys_from_credential_file(filename=default_credential_file, caching=true)
return {:access_key => @access_key, :secret_access_key => @secret_access_key} if (@access_key && @secret_access_key && caching)
return {} if filename.nil? or not File.exists?(filename)
puts("Reading keys from file: #{filename}")
File.open(filename).each_line do |line|
if line =~ /AWSAccessKeyId=([a-zA-Z0-9]+)$/
@access_key=$1.chomp
elsif line =~ /AWSSecretKey=([^ ]+)$/
@secret_access_key=$1.chomp
end
end
return {:access_key => @access_key, :secret_access_key => @secret_access_key}
end
default_options(
:instance_type => 'm1.small',
:availability_zones => ["us-east-1a"],
:user_id => default_user_id,
:private_key => default_private_key,
:cert => default_cert,
:cloud_cert => default_cloud_cert,
:access_key => default_access_key,
:secret_access_key => default_secret_access_key,
:ec2_url => default_ec2_url,
:s3_url => default_s3_url,
:credential_file => default_credential_file,
:min_count => 1,
:max_count => 1,
:user_data => '',
:kernel_id => nil,
:ramdisk_id => nil,
:block_device_mapping => [{}],
:subnet_id => nil,
:spot_price => nil,
:launch_group => nil,
:spot_persistence => nil,
:disable_api_termination => nil,
:instance_initiated_shutdown_behavior => nil
)
# Called when the create command is called on the cloud
def create!
[:security_groups, :load_balancers, :rds_instances].each do |type|
self.send(type).each {|ele| ele.create! }
end
end
def run
puts " for cloud: #{cloud.name}"
puts " minimum_instances: #{minimum_instances}"
puts " maximum_instances: #{maximum_instances}"
puts " security_groups: #{security_group_names.join(", ")}"
puts " image id: #{image_id}"
puts " using keypair: #{keypair}"
puts " with user_data: #{user_data.to_s.inspect[0..100]} ..."
puts " user: #{user}"
puts " at spot price: #{spot_price} #{spot_persistence}\n" if spot_price
security_groups.each do |sg|
sg.run
end
unless rds_instances.empty?
rds_instances.each do |rdsi|
puts " rds instance: #{rdsi.name}"
rdsi.run
end
end
if autoscalers.empty? # not using autoscaling
puts "---- live, running instances (#{nodes.size}) ----"
if nodes.size < minimum_instances
expansion_count = minimum_instances - nodes.size
puts "-----> expanding the cloud because the #{expansion_count} minimum_instances is not satisified: "
maybe('expand cloud') do
expand_by(expansion_count)
end
elsif nodes.size > maximum_instances
contraction_count = nodes.size - maximum_instances
puts "-----> contracting the cloud because the instances count exceeds the #{maximum_instances} maximum_instances by #{contraction_count}"
maybe('contract cloud') do
contract_by(contraction_count)
end
end
progress_bar_until("Waiting for the instances to be launched") do
reset!
running_nodes = nodes.select {|n| n.running? }
running_nodes.size >= minimum_instances
end
reset!
# ELASTIC IPS
else
autoscalers.each do |a|
puts " autoscaler: #{a.name}"
puts "-----> The autoscaling groups will launch the instances"
a.run
progress_bar_until("Waiting for autoscaler to launch instances") do
reset!
running_nodes = nodes.select {|n| n.running? }
running_nodes.size >= minimum_instances
end
reset!
end
end
from_ports = security_groups.map {|a| a.authorizes.map {|t| t.from_port.to_i }.flatten }.flatten
if from_ports.include?(22)
progress_bar_until("Waiting for the instances to be accessible by ssh") do
running_nodes = nodes.select {|n| n.running? }
accessible_count = running_nodes.map do |node|
node.accessible?
end.size
accessible_count == running_nodes.size
end
end
unless load_balancers.empty?
load_balancers.each do |lb|
puts " load balancer: #{lb.name}"
lb.run
end
end
assign_elastic_ips
cleanup_ssh_known_hosts!
puts "Attaching EBS volumes"
assign_ebs_volumes # Assign EBS volumes
end
def teardown
puts "------ Tearing down and cleaning up #{cloud.name} cloud"
unless autoscalers.empty?
puts "Tearing down autoscalers"
end
end
def expand_by(num=1)
e = Ec2Instance.run!({
:image_id => image_id,
:min_count => num,
:max_count => num,
:key_name => keypair.basename,
:security_groups => security_groups,
:user_data => decoded_user_data,
:instance_type => instance_type,
:availability_zone => availability_zones.first,
:base64_encoded => true,
:cloud => cloud,
:block_device_mapping => block_device_mapping,
:subnet_id => subnet_id,
:spot_price => spot_price,
:launch_group => launch_group,
:spot_persistence => spot_persistence,
:disable_api_termination => disable_api_termination,
:instance_initiated_shutdown_behavior => instance_initiated_shutdown_behavior
})
return if e == 'spot instances requested'
progress_bar_until("Waiting for node to launch...") do
wait_for_node(e)
end
all_nodes.detect {|n| n.instance_id == e.instance_id }
end
def decoded_user_data
if user_data
if File.file?(user_data)
open(user_data).read
else
user_data
end
end
end
def wait_for_node(instance)
reset!
inst = all_nodes.detect {|n| n.instance_id == instance.instance_id }
inst.running? if inst
end
def contract_by(num=1)
raise RuntimeError, "Contracting instances by #{num} will lower the number of instances below specified minimum" unless nodes.size - num > minimum_instances
num.times do |i|
node = nodes[-num]
id = node.instance_id
node.ssh_cleanup_known_hosts!
Ec2Instance.terminate!(:instance_id => id, :cloud => cloud)
end
reset!
end
def bootstrap_nodes!(tmp_path=nil)
unless security_groups.map {|a| a.authorizes.map {|t| t.from_port.to_i }.flatten }.flatten.include?(22)
warn "Cloud security_groups are not authorized for ssh. Cannot bootstrap."
return
end
tmp_path ||= cloud.tmp_path
nodes.each do |node|
next unless node.in_service?
node.cloud_provider = self
node.rsync_dir(tmp_path)
node.bootstrap_chef!
node.run_chef!
end
end
def configure_nodes!(tmp_path=nil)
# removed duplicated code (now configure_nodes! invokes
# node.bootstrap_chef!, while old version did not, but I believe
# this is harmless)
bootstrap_nodes!(tmp_path)
ebs_volume_groups.each do |vol_grp|
vol_grp.verify_attachments nodes
end
end
def assign_elastic_ips
unless elastic_ips.empty?
unused_elastic_ip_addresses = ElasticIp.unused_elastic_ips(self).map {|i| i.public_ip }
used_elastic_ip_addresses = ElasticIp.elastic_ips(self).map {|i| i.public_ip }
elastic_ip_objects = ElasticIp.unused_elastic_ips(self).select {|ip_obj| elastic_ips.include?(ip_obj.public_ip) }
assignee_nodes = nodes.select {|n| !ElasticIp.elastic_ips(self).include?(n.public_ip) }
elastic_ip_objects.each_with_index do |eip, idx|
# Only get the nodes that do not have elastic ips associated with them
begin
if assignee_nodes[idx]
puts "Assigning elastic ip: #{eip.public_ip} to node: #{assignee_nodes[idx].instance_id}"
ec2.associate_address(:instance_id => assignee_nodes[idx].instance_id, :public_ip => eip.public_ip)
end
rescue Exception => e
p [:error, e.inspect]
end
reset!
end
end
end
def cleanup_ssh_known_hosts!(nodes_to_cleanup=nodes,
even_unavailable=false)
puts "cleaning up .ssh/known_hosts"
nodes_to_cleanup.find_all do |node|
even_unavailable || node.ssh_available?
end.each do |node|
node.ssh_cleanup_known_hosts!
end
end
def nodes
all_nodes.select {|i| i.in_service? }#describe_instances.select {|i| i.in_service? && security_groups.include?(i.security_groups) }
end
# === Description
#
# Return all the security groups of the instance that are prefixed with #poolparty.
#
# These are special security groups used only for tagging
#
# === Parameters
# instance - An ec2 instance as returned from describe_instances
def tags(instance)
return [] if instance.groupSet.nil? || instance.groupSet.item.empty?
instance.groupSet.item.collect{|g| g.groupId }.select {|s| s.start_with? "#poolparty"}
end
def all_nodes
@nodes ||= describe_instances.select { |i|
!(security_group_names & tags(i)).empty?
}.sort {|a,b|
DateTime.parse(a.launchTime) <=> DateTime.parse(b.launchTime)
}
end
# Describe instances
# Describe the instances that are available on this cloud
# @params id (optional) if present, details about the instance
# with the id given will be returned
# if not given, details for all instances will be returned
def describe_instances(id=nil)
begin
@describe_instances = ec2.describe_instances.reservationSet.item.map do |r|
r.instancesSet.item.map do |i|
inst_options = i.merge(r.merge(:cloud => cloud)).merge(cloud.cloud_provider.dsl_options)
Ec2Instance.new(inst_options)
end
end.flatten
rescue AWS::InvalidClientTokenId => e # AWS credentials invalid
puts "Error contacting AWS: #{e}"
raise e
rescue Exception => e
[]
end
end
# Extras!
def block_device_mapping(o=[], given_name=cloud.proper_name )
@mappings ||= o
end
def load_balancer(given_name=cloud.proper_name, o={}, &block)
load_balancers << ElasticLoadBalancer.new(given_name, sub_opts.merge(o || {}), &block)
end
def autoscale(given_name=cloud.proper_name, o={}, &block)
autoscalers << ElasticAutoScaler.new(given_name, sub_opts.merge(o || {}), &block)
end
def security_group(given_name=cloud.proper_name, o={}, &block)
existing_security_group = security_groups.detect{|grp| grp.name == given_name }
if existing_security_group
existing_security_group.merge!(sub_opts.merge(o || {}), &block)
else
security_groups << SecurityGroup.new(given_name, sub_opts.merge(o || {}), &block)
end
end
def elastic_ip(*ips)
ips.each {|ip| elastic_ips << ip}
end
def rds(given_name=cloud.proper_name, o={}, &block)
rds_instances << RdsInstance.new(given_name, sub_opts.merge(o || {}), &block)
end
def available_rds_instances
rds_instances.select{|r| r.available? }
end
# prepare options for AWS gem
def aws_options(opts={})
uri=URI.parse(ec2_url)
{ :access_key_id => access_key,
:secret_access_key=> secret_access_key,
:use_ssl => (uri.scheme=='https'),
:path => uri.path,
:host => uri.host,
:port => uri.port
}.merge(opts)
end
# Proxy to the raw Grempe amazon-aws @ec2 instance
def ec2
@ec2 ||= begin
AWS::EC2::Base.new( aws_options )
rescue AWS::ArgumentError => e # AWS credentials missing?
puts "Error contacting AWS: #{e}"
raise e
rescue Exception => e
puts "Generic error #{e.class}: #{e}"
end
end
# Proxy to the raw Grempe amazon-aws autoscaling instance
def as
@as = AWS::Autoscaling::Base.new( aws_options )
end
# Proxy to the raw Grempe amazon-aws elastic_load_balancing instance
def elb
@elb ||= AWS::ELB::Base.new( aws_options )
end
def awsrds
@awsrds ||= AWS::RDS::Base.new( aws_options )
end
def security_group_names
security_groups.map {|a| a.to_s }
end
def security_groups
@security_groups ||= []
end
def load_balancers
@load_balancers ||= []
end
def autoscalers
@autoscalers ||= []
end
def elastic_ips
@elastic_ips ||= []
end
def ebs_volume_groups
@ebs_volume_groups ||= []
end
# dsl method for EBS volumes. E.G.:
# ebs_volumes do
# volumes "vol-001248ff", "vol-01ff4b85" # use existing volumes, not mandatory
# device "/dev/sdf"
# snapshot_id "snap-602030dd"
# size 200
# end
def ebs_volumes(name=nil, &block)
ebs_volume_groups << ElasticBlockStoreGroup.new(sub_opts,&block) if block
end
def assign_ebs_volumes
ebs_volume_groups.each{|ebs_volume_group| ebs_volume_group.attach(nodes)}
end
def rds_instances
@rds_instances ||= []
end
# Clear the cache
def reset!
@nodes = @describe_instances = nil
end
# Get existing volumes on EC2. filters is a hash of filters, either single valued or multivalued (value is an array of possible values).
# The function will return volumes matching *all* filters. A volume is a filter match if *any* one of the filter values equals the volume parameter value.
def list_ec2_volumes(filters=nil)
@volumes_on_ec2=ec2.describe_volumes.volumeSet.item unless @volumes_on_ec2
(if filters.nil? # no filter to check, so return at once
@volumes_on_ec2
else
@volumes_on_ec2.select{|vol| # select volumes for which no filter failed
not filters.map {|filter_key, filter_val|
filter_key=filter_key.to_s if filter_key.is_a?(Symbol) # filter_key may be given as a symbol
raise ArgumentError, "Filter key #{filter_key} is invalid" unless vol.has_key?(filter_key)
if filter_val.is_a?(Array) # Deal with multiple filter values
filter_val.map{|val| val.is_a?(String) ? val : val.to_s}.member?(vol[filter_key]) # make sure fiter_val array values are Strings before checking for match
else
(filter_val.is_a?(String) ? filter_val : filter_val.to_s)==vol[filter_key] # make sure fiter_val is a String before comparing
end
}.member?(false) # Check if a filter failed, the 'not' statement at the beginning of the map block negates this so 'select' will choose only when no filter failed
}.compact # remove nil results from volume set.
end
).map{|vol| ElasticBlockStore.new(vol,:cloud => cloud)}
end
# Read credentials from credential_file if one exists
def credential_file(file=nil)
unless file.nil?
dsl_options[:credential_file]=file
dsl_options.merge!(Ec2.load_keys_from_credential_file(file))
else
fetch(:credential_file)
end
end
private
# Helper to get the options with self as parent
def sub_opts
dsl_options.merge(:parent => self, :cloud => cloud)
end
def generate_keypair(n=nil)
puts "[EC2] generate_keypair is called with #{default_keypair_path/n}"
begin
hsh = ec2.create_keypair(:key_name => n)
string = hsh.keyMaterial
FileUtils.mkdir_p default_keypair_path unless File.directory?(default_keypair_path)
puts "[EC2] Generated keypair #{default_keypair_path/n}"
puts "[EC2] #{string}"
File.open(default_keypair_path/n, "w") {|f| f << string }
File.chmod 0600, default_keypair_path/n
rescue Exception => e
puts "[EC2] The keypair exists in EC2, but we cannot find the keypair locally: #{n} (#{e.inspect})"
end
keypair n
end
end
end
require "#{File.dirname(__FILE__)}/ec2_instance"
require "#{File.dirname(__FILE__)}/helpers/ec2_helper"
%w( security_group
authorize
elastic_auto_scaler
elastic_block_device_mapping
elastic_block_store
elastic_block_store_group
elastic_load_balancer
elastic_ip
rds_instance
revoke).each do |lib|
require "#{File.dirname(__FILE__)}/helpers/#{lib}"
end
================================================
FILE: lib/cloud_providers/ec2/ec2_instance.rb
================================================
module CloudProviders
class Ec2Instance < RemoteInstance
default_options(
:security_groups => [],
:private_ip => nil,
:dns_name => nil,
:instance_type => nil,
:public_ip => nil,
:key_name => nil,
:launch_time => nil,
:availability_zones => [],
:block_device_mapping => [{}],
:subnet_id => nil,
:spot_price => nil,
:launch_group => nil,
:spot_persistence => nil,
:disable_api_termination => nil,
:instance_initiated_shutdown_behavior => nil
)
def initialize(raw_response={})
@raw_response = raw_response
self.instance_id = raw_response["instanceId"] rescue nil
self.security_groups = raw_response.groupSet.item.map{|sg| sg.groupId }.sort rescue nil
self.image_id = raw_response["imageId"] rescue nil
self.private_ip = raw_response["privateIpAddress"] rescue nil
self.dns_name = raw_response["dnsName"] rescue nil
self.instance_type = raw_response["instanceType"] rescue nil
self.public_ip = raw_response["ipAddress"] rescue nil
self.key_name = raw_response["keyName"] rescue nil
self.launch_time = raw_response["launchTime"] rescue nil
self.availability_zones = raw_response["placement"]["availabilityZone"] rescue nil
self.status = raw_response["instanceState"]["name"] rescue nil
self.block_device_mapping = raw_response["blockDeviceMapping"] rescue nil
self.subnet_id = raw_response["subnetId"] rescue nil
self.launch_group = raw_response["launchGroup"] rescue nil
# disable_api_termination and instance_initiated_shutdown_behavior don't currently get returned in the request -- you'd need to later call describe_instance_attribute
self.disable_api_termination = raw_response["disableApiTermination"] rescue nil
self.instance_initiated_shutdown_behavior = raw_response["instanceInitiatedShutdownBehavior"] rescue nil
super
end
def keypair(n=nil)
return @keypair if @keypair
@keypair = (cloud.keypair.basename == self.key_name) ? cloud.keypair : Keypair.new(self.key_name, cloud.keypair.extra_paths)
end
def security_group_names
security_groups.map{|a| a.to_s }
end
def zone
availability_zones.first
end
def reachable?
ping_port self.public_ip, 22
end
def ssh_available?
cloud.security_groups.map {|a|
a.authorizes.map {|t| t.from_port.to_i }.flatten
}.flatten.include?(22) and
reachable? and
in_service? and
keypair and keypair.exists?
end
def in_service?
running?
end
def run!
if spot_price.to_f > 0
request_spot_instances!
else
launch_instances!
end
end
def self.run!(hsh); new(hsh).run!; end
def launch_instances!
r = cloud_provider.ec2.run_instances(
:image_id => image_id,
:min_count => min_count,
:max_count => max_count,
:key_name => keypair.basename,
:security_group => cloud.security_group_names,
:user_data => user_data,
:instance_type => instance_type,
:availability_zone => availability_zone,
:block_device_mapping => block_device_mapping,
:disable_api_termination => disable_api_termination,
:instance_initiated_shutdown_behavior => instance_initiated_shutdown_behavior,
:base64_encoded => true)
r.instancesSet.item.map do |i|
inst_options = i.merge(r.merge(:cloud => cloud)).merge(cloud.cloud_provider.dsl_options)
Ec2Instance.new(inst_options)
end.first
end
def request_spot_instances!
r = cloud_provider.ec2.request_spot_instances(
:spot_price => spot_price.to_s,
:launch_group => launch_group.to_s,
:instance_count => max_count,
:type => spot_persistence,
# TODO: valid_from, valid_until, availability_zone_group
:image_id => image_id,
:key_name => keypair.basename,
:security_group => cloud.security_group_names,
:user_data => user_data,
:instance_type => instance_type,
:availability_zone => availability_zone,
:block_device_mapping => block_device_mapping,
:launch_group => launch_group,
:base64_encoded => true)
p r
return 'spot instances requested'
end
def terminate!
cloud_provider.ec2.terminate_instances(:instance_id => [self.instance_id])
cloud_provider.reset!
end
def self.terminate!(hsh={}); new(hsh).terminate!; end
# list of directories and files to exclude when bundling an instance
def rsync_excludes(array_of_abs_paths_to_exclude=nil)
array_of_abs_paths_to_exclude ||= %w[
/sys
/proc
/dev/pts
/dev
/media
/mnt
/proc
/sys
/etc/ssh/ssh_host_*
/etc/ssh/moduli
/etc/udev/rules.d/70-persistent-net.rules
/etc/udev/rules.d/z25_persistent-net.rules
]
array_of_abs_paths_to_exclude.inject(''){|str, path| str << "--exclude=#{path}" ; str}
end
# create an image file and copy this instance to the image file.
def make_image(opts={})
opts = {:volume => '/',
:size => 6000,
:destination => '/mnt/bundle',
:exclude => nil
}.merge(opts)
image_file = File.join(opts[:destination], opts[:prefix] )
cmds = ["mkdir -p #{opts[:destination]}"]
cmds << "dd if=/dev/zero of=#{image_file} bs=1M count=#{opts[:size]}"
cmds << "mkfs.ext3 -F -j #{image_file}"
cmds << "mkdir -p #{opts[:destination]}/loop"
cmds << "mount -o loop #{image_file} #{opts[:destination]}/loop"
cmds << "rsync -ax #{rsync_excludes(opts[:exclude])} #{opts[:volume]}/ #{opts[:destination]}/loop/"
cmds << "if [[ -f /etc/init.d/ec2-ssh-host-key-gen ]]; then chmod u+x /etc/init.d/ec2-ssh-host-key-gen ;fi"
cmds << "umount #{opts[:destination]}/loop"
self.ssh cmds
image_file
end
# TODO: WIP: bundle up the instance and register it as a new ami.
# An image of the running node will be creatd, or
# if a path to an image file on the remote node is given, that will be used
def bundle_and_register(img=nil, opts={})
opts = {:cert => cert,
:bucket => nil,
:prefix => image_id,
:kernel => kernel_id,
:ramdisk => ramdisk_id,
:ec2cert => cloud_cert
}.merge(opts)
raise "You must specify a bucket to bundle to" if opts[:bucket].nil?
scp ec2cert, "/mnt/bundle/"
scp cert, "/mnt/bundle/"
arch = self[:instanceType].match(/m1\.small|c1\.medium/) ? 'i386' : 'x86_64'
image = img ? img : make_image(opts)
ssh "ec2-bundle-image #{image} -d /mnt/bundle -u #{self[:ownerId]} -k /mnt/bundle/pk-*.pem -c /tmp/cert-*.pem"
manifest = "/mnt/bundle/#{opts[:prefix]}.manifest.xml"
ssh "ec2-upload-bundle -a #{access_key} -s #{secret_access_key} -m #{manifest}"
ami_str = ssh "ec2-register-bundle"
ami = ami_str.grep(/ami-\w*/).first
return ami
end
end
end
================================================
FILE: lib/cloud_providers/ec2/helpers/authorize.rb
================================================
module CloudProviders
class Authorize < Ec2Helper
default_options({
:protocol => "tcp",
:from_port => "22",
:to_port => "22",
:network => "0.0.0.0/0",
:group_name => nil,
:owner_id => nil})
def run
options =
if group_name
puts "Authorizing #{name} for group named: #{group_name} of owner id: #{owner_id}"
{:group_name => name, :source_security_group_name=> group_name, :source_security_group_owner_id => owner_id}
else
puts "Authorizing: #{name} for #{protocol} to #{from_port}:#{to_port} #{network}"
to_hash
end
begin
ec2.authorize_security_group_ingress(options)
rescue AWS::InvalidPermissionDuplicate => e
nil
end
end
def to_hash
if group_name
{:group_name => group_name}
else
{
:group_name => name,
:ip_protocol => protocol,
:from_port => from_port,
:to_port => to_port,
:cidr_ip => network
}
end
end
end
end
================================================
FILE: lib/cloud_providers/ec2/helpers/ec2_helper.rb
================================================
module CloudProviders
class Ec2Helper < CloudProvider
def initialize(name=cloud.proper_name, init_opts={}, &block)
@name = name
if name.is_a?(Hash)
@name = name[:name]
@init_opts = name if init_opts.empty?
else
@init_opts = init_opts
end
set_vars_from_options(init_opts)
instance_eval &block if block
after_initialized
end
def elb
cloud.elb
end
def ec2
cloud.ec2
end
def as
cloud.as
end
def rds
cloud.awsrds
end
def pool
cloud.parent
end
def self.property(*names)
names.each do |name|
define_method name do |*args|
instance_variable_set("@#{name}", args.first) unless args.empty?
instance_variable_get("@#{name}")
end
end
end
end
end
================================================
FILE: lib/cloud_providers/ec2/helpers/elastic_auto_scaler.rb
================================================
module CloudProviders
class ElasticAutoScaler < Ec2Helper
default_options(
:cooldown => nil,
:desired_capacity => nil
)
def run
if should_create_autoscaling_group?
puts "Creating autoscaling group"
create_launch_configuration!
create_autoscaling_group!
else
puts "-----> Checking for launch configuration named: #{old_launch_configuration_name}"
if should_create_launch_configuration?
create_launch_configuration!
elsif should_update_launch_configuration? || should_update_autoscaling_group?
update_launch_configuration!
puts "Updating autoscaling group"
update_autoscaling_group!
puts "Deleting old launch configuration: #{old_launch_configuration_name}"
as.delete_launch_configuration(:launch_configuration_name => old_launch_configuration_name)
end
end
triggers.each do |trigger|
trigger.run
end
end
# First, change the min_count to
def teardown
triggers.each do |trigger|
trigger.teardown
end
if autoscaling_groups.select {|n| n.name == name }.empty?
puts "Cloud #{cloud.name} autoscaling group does not exist"
else
self.minimum_instances = 0
self.maximum_instances = 0
@new_launch_configuration_name = old_launch_configuration_name
puts "Updating autoscaling group: #{@new_launch_configuration_name}"
update_autoscaling_group!
puts "Terminating nodes in autoscaling group: #{name}"
reset!
# cloud.nodes.each {|n| n.terminate! }
delete_autoscaling_group!
delete_launch_configuration!
puts ""
end
end
private
def delete_autoscaling_group!
ensure_no_scaling_activities
reset!
begin
as.delete_autoscaling_group(:autoscaling_group_name => name)
rescue AWS::Error => e
if e.message =~ /You cannot delete an AutoScalingGroup while there are scaling activities in progress/
delete_autoscaling_group!
end
rescue Exception => e
p e.inspect
end
end
def delete_launch_configuration!(n=new_launch_configuration_name)
ensure_no_scaling_activities
as.delete_launch_configuration(:launch_configuration_name => n)
end
def ensure_no_scaling_activities
# loop do
# reset!
# activities = scaling_activities.select {|a| !a[:complete] }
# running_nodes = cloud.nodes.select {|n| n.running? }
# if activities.empty? && running_nodes.empty?
# break
# else
# $stdout.print "."
# $stdout.flush
# sleep 1
# end
# end
progress_bar_until do
reset!
activities = scaling_activities.select {|a| !a[:complete] }
running_nodes = cloud.nodes.select {|n| n.running? }
activities.empty? && running_nodes.empty?
end
reset!
end
public
def should_create_autoscaling_group?
known = autoscaling_groups.select {|ag| ag.name == cloud.proper_name }
if known.empty?
true
else
puts "Autoscaling group already defined...: #{name}"
false
end
end
def should_create_launch_configuration?
known = launch_configurations.select {|lc| lc.name =~ /#{name}/ }
if known.empty?
true
else
false
end
end
def should_update_launch_configuration?
known = launch_configurations.select {|lc| lc.name =~ /#{name}/ }
if known.empty?
true
else
differences = known.map do |k|
t = k.diff({
:image_id => image_id,
:instance_type => instance_type,
:security_groups => parent.security_group_names.flatten,
:key_name => keypair.to_s,
:user_data => user_data,
}, :user_data, :image_id, :instance_type, :security_groups, :key_name)
t.empty? ? nil : t
end.reject {|a| a.nil? }
if differences.empty?
false
else
true
end
end
end
def update_launch_configuration!
create_launch_configuration!
end
def create_launch_configuration!(lc=new_launch_configuration_name)
puts "-----> Creating launch configuration: #{new_launch_configuration_name} for #{proper_name}"
begin
@launch_configuration_name = lc
as.create_launch_configuration({
:launch_configuration_name => new_launch_configuration_name,
:image_id => image_id,
:instance_type => instance_type,
:security_groups => parent.security_group_names,
:key_name => keypair.to_s,
:user_data => user_data,
:kernel_id => kernel_id,
:ramdisk_id => ramdisk_id,
:block_device_mapping => block_device_mapping
})
rescue Exception => e
puts <<-EOE
-----> There was an error: #{e.inspect} when creating the launch_configurations
EOE
ensure
reset!
end
end
def launch_configurations
begin
@launch_configurations ||= as.describe_launch_configurations.DescribeLaunchConfigurationsResult.LaunchConfigurations.member.map do |a|
{
:name => a["LaunchConfigurationName"],
:ramdisk_id => a["RamdiskId"],
:image_id => a["ImageId"],
:security_groups => (a["SecurityGroups"]["member"] rescue ["default"]),
:created_time => a["CreatedTime"],
:user_data => a["UserData"] || "",
:key_name => a["KeyName"],
:instance_type => a["InstanceType"]
}
end
rescue Exception => e
[]
end
end
def create_autoscaling_group!
as.create_autoscaling_group({
:autoscaling_group_name => name,
:availability_zones => availability_zones,
:launch_configuration_name => new_launch_configuration_name,
:min_size => minimum_instances.to_s,
:max_size => maximum_instances.to_s,
:load_balancer_names => load_balancers.map {|lb| lb.name }
})
reset!
end
def should_update_autoscaling_group?
known = autoscaling_groups.select {|lc| lc.name =~ /#{name}/ }
if known.empty?
true
else
differences = known.map do |k|
hsh = {
:min_size => minimum_instances.to_s, :max_size => maximum_instances.to_s,
:availability_zones => availability_zones,
:launch_configuration_name => old_launch_configuration_name
}
hsh.merge!(:cooldown => cooldown.to_s) if cooldown
t = hsh.diff(k, :cooldown,
:min_size,
:max_size,
:availability_zones,
:launch_configuration_name)
t.empty? ? nil : t
end.reject {|a| a.nil? }
if differences.empty?
false
else
puts "-----> Recreating the autoscaling group as details have changed: #{differences.inspect}"
true
end
end
end
def update_autoscaling_group!
as.update_autoscaling_group(
:autoscaling_group_name => name,
:availability_zones => availability_zones.first, # TODO: Figure out how to support multiple availability_zones
:launch_configuration_name => new_launch_configuration_name,
:min_size => minimum_instances.to_s,
:max_size => maximum_instances.to_s,
:cooldown => cooldown.to_s,
:load_balancer_names => load_balancers.map {|lb| lb.name }
)
end
def autoscaling_groups
@autoscaling_groups ||= as.describe_autoscaling_groups.DescribeAutoScalingGroupsResult.AutoScalingGroups.member.map do |g|
{
:cooldown => g["Cooldown"],
:desired_capacity => g["DesiredCapacity"],
:created_time => g["CreatedTime"],
:min_size => g["MinSize"],
:max_size => g["MaxSize"],
:load_balancer_names => (g["LoadBalancerNames"]["member"] rescue []),
:availability_zones => (g["AvailabilityZones"]["member"] rescue []),
:launch_configuration_name => g["LaunchConfigurationName"],
:name => g["AutoScalingGroupName"],
:instances => (g["Instances"]["member"] rescue []).map {|i|
{:instance_id => i["InstanceId"],
:state => i["LifecycleState"],
:availability_zone => i["AvailabilityZone"]
}}
}
end rescue []
end
def scaling_activities
@scaling_activities ||= as.describe_scaling_activities(:autoscaling_group_name => name).DescribeScalingActivitiesResult.Activities.member.map do |action|
{
:cause => action["Cause"],
:progress => action["Progress"].to_i,
:activity_id => action["ActivityId"],
:description => action["Description"],
:status_code => action["StatusCode"],
:complete => action["StatusCode"] == "Pending" ? false : true,
:start_time => action["StartTime"]
}
end rescue []
end
# Temporary names so we can create and recreate launch_configurations
def new_launch_configuration_name
@new_launch_configuration_name ||= "#{name}#{used_launched_config_id.zero? ? 1 : 0}"
end
def old_launch_configuration_name
@old_launch_configuration_name ||= "#{name}#{used_launched_config_id.zero? ? 0 : 1}"
end
# Compute the next configuration launch id. We'll be cycling through the usage of 0 and 1
# Here we are just looking for which one that is, either zero or 1
def used_launched_config_id
return @used_launched_config_id if @used_launched_config_id
used_configuration_names = launch_configurations.map {|hsh| hsh[:name] =~ /#{name}/ ? hsh[:name] : nil }.reject {|a| a.nil?}
used_launched_config_id = used_configuration_names.map {|a| a.gsub(/#{name}/, '').to_i }.reject {|a| a.zero? }.first
used_launched_config_id = 0 if used_launched_config_id.nil?
@used_launched_config_id = used_launched_config_id
end
def new_auto_scaling_group_name
@new_auto_scaling_group_name ||= "#{name}#{used_autoscaling_group_id.zero? ? 1 : 0}"
end
def old_auto_scaling_group_name
@old_auto_scaling_group_name ||= "#{name}#{used_autoscaling_group_id.zero? ? 0 : 1}"
end
def used_autoscaling_group_id
return @used_autoscaling_group_id if @used_autoscaling_group_id
used_autoscaling_groups = launch_configurations.map {|hsh| hsh[:name] =~ /#{name}/ ? hsh[:name] : nil }.reject {|a| a.nil?}
used_autoscaling_group_id = used_autoscaling_groups.map {|a| a.gsub(/#{name}/, '').to_i }.reject {|a| a.zero? }.first
used_autoscaling_group_id = 0 if used_autoscaling_group_id.nil?
@used_autoscaling_group_id ||= used_autoscaling_group_id
end
# Convenience methods
# Return an array of hashes describing the autoscaling groups
def list
describe_autoscaling_groups.DescribeAutoScalingGroupsResult.AutoScalingGroups.member
end
def trigger(*trigger_hashes)
trigger_hashes.each do |hsh, blk|
triggers << ElasticTrigger.new(name, hsh.merge(:cloud => cloud), &blk)
end
end
private
def triggers
@triggers ||= []
end
def reset!
@old_auto_scaling_group_name =
@new_auto_scaling_group_name =
@autoscaling_groups = @scaling_activities =
@launch_configurations = nil
cloud.reset!
end
end
class ElasticTrigger < Ec2Helper
default_options(
:measure => :cpu,
:period => 60,
:statistic => :average,
:unit => "Percent",
:lower_threshold => 20,
:upper_threshold => 60,
:trigger_name => "CpuTrigger",
:lower_breach_scale_increment => -1,
:upper_breach_scale_increment => 1,
:breach_duration => 120,
:namespace => 'AWS/EC2'
)
def measure_names
{:cpu => "CPUUtilization"}
end
def statistic_names
{:min => "Minimum", :max => "Maximum", :average => "Average", :sum => "Sum"}
end
def run
if autoscaling_triggers.empty?
create_autoscaling_trigger!
else
t = autoscaling_triggers.map do |hsh|
diff(hsh)
end.flatten
unless t.empty?
puts "Creating or updating trigger: #{trigger_name}"
create_autoscaling_trigger!
end
end
end
def teardown
autoscaling_triggers.each do |trigger|
puts "Deleting trigger: #{trigger[:trigger_name]}"
as.delete_trigger(:trigger_name => trigger[:trigger_name], :autoscaling_group_name => name)
end
end
def diff(hsh={})
[ :measure_name,
:period,
:statistic,
:lower_threshold,
:lower_breach_scale_increment,
:upper_threshold,
:upper_breach_scale_increment,
:unit,
:trigger_name].reject do |k|
hsh[k].to_s.capitalize == self.send(k).to_s.capitalize
end
end
private
def autoscaling_triggers
begin
as.describe_triggers(:autoscaling_group_name => name).DescribeTriggersResult.Triggers.member.map do |trigger|
{
:trigger_name => trigger["TriggerName"],
:statistic => trigger["Statistic"],
:status => trigger["Status"],
:lower_threshold => trigger["LowerThreshold"],
:created_time => trigger["CreatedTime"],
:measure_name => trigger["MeasureName"],
:upper_threshold => trigger["UpperThreshold"],
:lower_breach_scale_increment => trigger["LowerBreachScaleIncrement"],
:period => trigger["Period"],
:upper_breach_scale_increment => trigger["UpperBreachScaleIncrement"],
:breach_duration => trigger["BreachDuration"],
:dimensions => trigger["Dimensions"],
:unit => trigger["Unit"],
:autoscaling_group_name => trigger["AutoScalingGroupName"],
:namespace => trigger['Namespace']
}
end
rescue Exception => e
[]
end
end
def create_autoscaling_trigger!
as.create_or_updated_scaling_trigger(
:autoscaling_group_name => name,
:dimensions => {:name => "AutoScalingGroupName", :value => name},
:measure_name => measure_name,
:period => "#{period}",
:trigger_name => trigger_name,
:statistic => (statistic_names[statistic] || statistic),
:unit => unit,
:breach_duration => breach_duration,
:lower_threshold => "#{lower_threshold}",
:lower_breach_scale_increment => "#{lower_breach_scale_increment}",
:upper_threshold => "#{upper_threshold}",
:upper_breach_scale_increment => "#{upper_breach_scale_increment}",
:namespace => namespace
)
end
def measure_name
measure_names[measure] || measure
end
def trigger_name
"#{(measure_names[measure] || measure)}-#{name}" || name
end
end
end
================================================
FILE: lib/cloud_providers/ec2/helpers/elastic_block_device_mapping.rb
================================================
module CloudProviders
class ElasticBlockDeviceMapping < Ec2Helper
def initialize(name, init_opts={}, &block)
super
end
end
end
================================================
FILE: lib/cloud_providers/ec2/helpers/elastic_block_store.rb
================================================
module CloudProviders
class ElasticBlockStore < Ec2Helper
# instance methods
attr_reader :volumeId, :size, :snapshotId, :status, :attachments, :device, :availabilityZone, :instanceId
attr_reader :createTime
alias :volume_id :volumeId
alias :snapshot_id :snapshotId
alias :availability_zone :availabilityZone
alias :create_time :createTime
alias :instance_id :instanceId
def createTime(create_time)
unless create_time.class==DateTime
@create_time=(DateTime.new(create_time) rescue nil)
else
@createTime=create_time
end
end
def initialize(raw_response,init_opts={},&block)
parse_raw_response(raw_response)
super(volumeId,init_opts,&block)
end
def parse_raw_response(raw_response)
@raw_respons = raw_response
raw_response.each{|k,v| instance_variable_set("@"+k,v) if respond_to?(k) }
unless raw_response.attachmentSet.nil?
@attachments=raw_response.attachmentSet.item
@attachments.each{|attch| if attch.status=="attached" or attch.status=="attaching"
@instanceId=attch.instanceId
@device=attch.device
end
}
end
end
def attached?(fn_instance_id=nil)
return false unless @status=="in-use" or @status=="attaching"
return true if fn_instance_id.nil?
return true if fn_instance_id == instance_id
return false
end
def available?
@status=="available"
end
def attach(ec2_instance,device)
if ec2.attach_volume(:volume_id => volume_id, :instance_id => ec2_instance.instance_id, :device => device).return=="true"
update!
return true
end
false
end
def detach
if ec2.detach_volume(:volume_id => volume_id).return=="true"
update!
return true
end
false
end
def detach!
ec2.detach_volume(:volume_id => volume_id, :force => true).return=="true"
end
def delete!
ec2.delete(:volume_id => volume_id).return == "true"
end
def update!
parse_raw_response ec2.describe_volumes(:volume_id => volume_id)
end
end
end
================================================
FILE: lib/cloud_providers/ec2/helpers/elastic_block_store_group.rb
================================================
module CloudProviders
# ElasticBlockStoreGroup class allows easy manipulation of EBS volumes matching defined criterias.
# Existing volumes in *cloud*'s availability zones that match the criterias will be selected for the group. When cloud instances need to attach EBS volumes from the group, the attach method should called.
# When attaching volumes the ElasticBlockStoreGroup will select existing (unattached) volumes until there are non, afterwhich the group will create new volumes according to the criterias given as needed.
#
# Currently, EBS volumes will not be deleted when tearing down a cloud. This is because poolparty is stateless and thus deleting drives from it will probably result in catastroph (deletions will be too general and delete stuff you don't want deleted).
# Hopefully, we will come up with a scheme for a deletion flag of some sort to solve this situation.
class ElasticBlockStoreGroup < Ec2Helper
default_options(:device => nil, :size => 0, :snapshot_id => nil)
alias :snapshotId :snapshot_id
def initialize(name=cloud.proper_name, init_opts={}, &block)
@volumes=[]
super
end
def after_initialized
unless @volumes.size > 0
filters={:size => size, :availabilityZone => availability_zones}
filters[:snapshotId]=snapshot_id if snapshot_id
@volumes=cloud.list_ec2_volumes filters
end
end
def volumes(*volume_ids)
return @volumes if volume_ids.size==0
volume_ids.each{|volume_id| @volumes << cloud.list_ec2_volumes(:volumeId => volume_id)}
end
def volumes_attached_to(instanceId)
@volumes.select {|vol| vol.attached?(instanceId)}
end
# get volumes that are not attached
def free_volumes(availability_zone)
@volumes.flatten.select{|vol| vol.available? and vol.availability_zone == availability_zone}
end
# Get a free volume from existing volumes in group or create a new one
def get_free_volume(availability_zone)
free=free_volumes(availability_zone)
if free.size>=1
return free[0]
end
create(availability_zone)
end
# Create new volume on availability_zone
def create(availability_zone)
options={:availability_zone => availability_zone, :size => size.to_s}
options[:snapshot_id]=snapshot_id if snapshot_id
vol=ElasticBlockStore.new(ec2.create_volume(options),:cloud => cloud)
@volumes< nil, :public_ip => nil)
def initialize(raw_response={})
@raw_response = raw_response
self.instance_id = raw_response["instanceId"]
self.public_ip = raw_response["publicIp"]
end
def assign_to(node)
puts "-----> Assigning #{public_ip} to node: #{node.instance_id}"
end
def self.unused_elastic_ips(ec2_instance)
@unused_elastic_ips ||= elastic_ips(ec2_instance).select {|ip| ip.instance_id.nil? }
end
def self.elastic_ips(ec2_instance)
begin
@elastic_ips ||= ec2_instance.ec2.describe_addresses.addressesSet.item.map do |i|
new(i)
end
rescue Exception => e
[]
end
end
def self.reset!
@elastic_ips = @unused_elastic_ips = nil
end
end
end
================================================
FILE: lib/cloud_providers/ec2/helpers/elastic_load_balancer.rb
================================================
module CloudProviders
class ElasticLoadBalancer < Ec2Helper
default_options(
:listeners => []
)
def create!
if should_create_load_balancer?
puts "-----> Creating ElasticLoadBalancer: #{name}"
create_load_balancer!
end
end
def run
create! # Just for now, while we migrate to 2 commands
if should_update_load_balancer?
create_load_balancer!
end
_health_checks.each do |ck|
configure_health_check!(ck)
end
# Remove old nodes that are no longer alive
detach_instances_if_necessary
# Try to unregister and reregister nodes that are out of service, perhaps it was just a setup bug that the setup took too long
out_of_service_node_listing = instance_healths.select {|a| a[:state] == "OutOfService" }.map {|a| a[:instance_id] }
reset!
out_of_service_nodes = nodes.select {|n| out_of_service_node_listing.include?(n.instance_id)}
unless out_of_service_nodes.empty?
puts "Uh oh. Out of service instance!: #{out_of_service_nodes.inspect}"
elb.deregister_instances_from_load_balancer(:load_balancer_name => name, :instances => out_of_service_nodes)
elb.register_instances_with_load_balancer(:load_balancer_name => name, :instances => out_of_service_nodes)
end
# Attach new nodes if there are any new nodes
attach_instances_if_necessary
end
def teardown
puts "-----> Tearing down load balancer: #{name}"
elb.delete_load_balancer(:load_balancer_name => name)
end
def listener(*listener_hashes)
listener_hashes.each do |hsh|
_listeners << ElasticListener.new(hsh)
end
end
def health_check(*health_check_hashes)
health_check_hashes.each do |hsh|
_health_checks << HealthCheck.new(hsh)
end
end
private
def _listeners
@_listeners ||= []
end
def _health_checks
@_health_checks ||= []
end
def real_name
name
end
public
def attach_instances_if_necessary
parent.reset!
instances = parent.nodes.map {|a| a.instance_id } # ec2 gem requires this be an array of names
elb.register_instances_with_load_balancer(:instances => instances, :load_balancer_name => "#{name}") unless instances.empty?
end
def detach_instances_if_necessary
parent.reset!
begin
instances = parent.all_nodes.select {|a| !a.running? }.map {|a| a.instance_id }
elb.deregister_instances_from_load_balancer(:instances => instances, :load_balancer_name => "#{name}") unless instances.empty?
rescue Exception => e
end
end
def should_create_load_balancer?
elastic_load_balancers.select {|lb| lb.name == name }.empty?
end
def create_load_balancer!
elb.delete_load_balancer(:load_balancer_name => name)
elb.create_load_balancer(
:availability_zones => parent.availability_zones,
:load_balancer_name => real_name,
:listeners => _listeners.map {|l| l.to_hash }
)
end
def configure_health_check!(hc)
# puts "Configuring health_check: #{hc.to_hash.inspect}"
elb.configure_health_check(:health_check => hc.to_hash, :load_balancer_name => name)
end
def should_update_load_balancer?
known = elastic_load_balancers.select {|lc| lc.name =~ /#{name}/ }.flatten
if known.empty?
true
else
known_listeners = known.map {|a| a[:listeners]}.flatten
# Take the known listeners (that are defined on the cloud_provider)
# and compare their describable values to those that are defined in
# the clouds.rb. Select only those that are different.
differences = _listeners.reject do |listener|
known_listeners.reject {|kl| listener.diff(kl).empty? }.empty?
end.flatten
if differences.empty?
false
else
true
end
end
end
def running_load_balancers
elastic_load_balancers.select {|lc| lc.name =~ /#{name}/ }.flatten
end
def elastic_load_balancers
begin
@elastic_load_balancers ||= elb.describe_load_balancers.DescribeLoadBalancersResult.LoadBalancerDescriptions.member.map do |lb|
{
:created_time => lb["CreatedTime"],
:availability_zones => (lb["AvailabilityZones"]["member"] rescue []),
:dns_name => lb["DNSName"],
:name => lb["LoadBalancerName"],
:instances => (g["Instances"]["member"] rescue []).map {|i| {:instance_id => i["InstanceId"]}},
:health_check => ([lb["HealthCheck"]] rescue []).map do |hc|
{
:healthy_threshold => hc["HealthyThreshold"],
:timeout => hc["Timeout"],
:unhealthy_threshold => hc["UnhealthyThreshold"],
:interval => hc["Interval"],
:target => hc["Target"]
}
end,
:listeners => (lb["Listeners"]["member"] rescue []).map do |listener|
{
:instance_port => listener["InstancePort"],
:protocol => listener["Protocol"],
:load_balancer_port => listener["LoadBalancerPort"]
}
end
}
end
rescue Exception => e
[]
end
end
def instance_healths
@instance_healths ||=
begin
elb.describe_instance_health(:load_balancer_name => name).DescribeInstanceHealthResult.InstanceStates.member.map do |i|
{
:instance_id => i["InstanceId"],
:reason_code => i["ReasonCode"],
:state => i["State"],
:description => i["Description"]
}
end
rescue Exception => e
[]
end
end
end
class HealthCheck < Ec2Helper
default_options(
:target => "HTTP:80/",
:interval => 5,
:timeout => 3,
:unhealthy_threshold => 2,
:healthy_threshold => 2
)
def initialize(name, init_opts={}, &block)
set_vars_from_options(name)
super
end
def to_hash
{ :target => target,
:interval => interval.to_s,
:timeout => timeout.to_s,
:unhealthy_threshold => unhealthy_threshold.to_s,
:healthy_threshold => healthy_threshold.to_s}
end
end
class ElasticListener < Ec2Helper
default_options(
:instance_port => 80,
:load_balancer_port => 80,
:protocol => "http"
)
def initialize(name, init_opts={}, &block)
set_vars_from_options(name)
super
end
def to_hash
{:protocol => protocol, :load_balancer_port => load_balancer_port.to_s, :instance_port => instance_port.to_s}
end
def diff(hsh={})
[:protocol, :load_balancer_port, :instance_port].reject do |k|
hsh[k].to_s.capitalize == self.send(k).to_s.capitalize
end
end
end
end
================================================
FILE: lib/cloud_providers/ec2/helpers/rds_instance.rb
================================================
module CloudProviders
class RdsInstance < Ec2Helper
property :username, :password, :storage, :instance_class, :engine
def authorize(options)
options.each do |key, value|
authorizations[key.to_s] = value
end
end
def database(*db_names)
@databases ||= []
@databases = @databases + db_names unless db_names.empty?
@databases
end
alias_method :databases, :database
def create!
if should_create_rds_instance?
puts "-----> Creating RDS Instance: #{instance_id}"
create_rds_instance!
end
end
def run
create! # Just for now, while we migrate to 2 commands
authorize_access
# TODO : wait until accessible?
end
def teardown
puts "-----> Tearing down RDS Instance: #{instance_id}"
delete_rds_instance!
end
def current_status
rds_instances.detect{|i| i.DBInstanceIdentifier == instance_id }
end
def exists?
! current_status.nil?
end
def available?
exists? && current_status.DBInstanceStatus == 'available'
end
def instance_id
name.to_s
end
private
def authorizations
@authorizations ||= {}
end
def after_initialized
raise "username must be specified" if self.username.nil?
raise "password must be specified" if self.password.nil?
raise "invalid password format (letters and digits only)" unless self.password =~ /[a-z][a-z0-9]*/i
raise "EC2 user id must be defined in ENV or config" if Ec2.default_user_id.nil?
end
def rds_instances
@rds_instances ||= (rds.describe_db_instances.DescribeDBInstancesResult.DBInstances || {})['DBInstance'] || []
@rds_instances = [@rds_instances] unless @rds_instances.is_a?(Array)
@rds_instances
end
def should_create_rds_instance?
! exists?
end
def create_rds_instance!
db_name = (databases.shift || instance_id).to_s.gsub(/\-/, '_')
params = {
:db_instance_identifier => instance_id,
:allocated_storage => storage || 5,
:db_instance_class => instance_class || "db.m1.small",
:engine => engine || "MySQL5.1",
:master_username => username,
:master_user_password => password,
:db_name => db_name
}
# TODO : optional params : :port, :db_parameter_group, :db_security_groups, :availability_zone, :preferred_backup_window, :backend_retention_period
rds.create_db_instance(params)
# TODO : create additional databases
end
def delete_rds_instance!
rds.delete_db_instance(:db_instance_identifier => instance_id, :skip_final_snapshot => "true")
end
def authorize_access
authorizations[:security_groups] = cloud.security_groups.map{|sg| sg.name } if authorizations[:security_groups].nil?
authorizations.each do |type, values|
[*values].each do |value|
begin
# TODO : allow customization of db sec group name
params = {:db_security_group_name => "default"}
if type.to_s =~ /network/
puts "authorizing NET access for #{value}..."
params[:cidrip] = value
else
puts "authorizing SECGRP access for #{value}/#{Ec2.default_user_id}..."
params[:ec2_security_group_name] = value
params[:ec2_security_group_owner_id] = Ec2.default_user_id
end
rds.authorize_db_security_group(params)
rescue AWS::Error => e
raise e unless e.message =~ /Authorization already exists/
end
end
end
end
end
end
================================================
FILE: lib/cloud_providers/ec2/helpers/revoke.rb
================================================
module CloudProviders
class Revoke < Ec2Helper
default_options({
:protocol => "tcp",
:from_port => "22",
:to_port => "22",
:cidr_ip => "0.0.0.0/0"})
def run
puts "Revoking: #{name} for #{protocol} to #{from_port}:#{to_port} #{network}"
options = { :group_name => name,
:ip_protocol => protocol,
:from_port => from_port,
:to_port => to_port,
:cidr_ip => network}
ec2.revoke_security_group_ingress(options) rescue nil
end
alias :network :cidr_ip
end
end
================================================
FILE: lib/cloud_providers/ec2/helpers/security_group.rb
================================================
module CloudProviders
class SecurityGroup < Ec2Helper
def merge! init_opts, &block
set_vars_from_options(init_opts)
instance_eval &block if block
end
def run
if should_create_security_group?
create_security_group!
end
current_security_groups = security_groups.map {|a|
# Example:
# [{ :ip_permissions=>[
# {:ip_ranges=>[{:cidr_ip=>"0.0.0.0/0"}], :from_port=>"22", :protocol=>"tcp", :to_port=>"22"},
# {:ip_ranges=>[{:cidr_ip=>"0.0.0.0/0"}], :from_port=>"80", :protocol=>"tcp", :to_port=>"80"} ],
# :description=>"PoolParty generated security group: clyde-chefclient", :name=>"clyde-chefclient"}]
#
a[:ip_permissions].map do |perm|
if perm[:group_name]
{
:group_name => perm[:group_name]
}
else
(perm[:ip_ranges] || ["0.0.0.0/0"]).map do |range|
range = range[:cidr_ip] if range.is_a?(Hash)
{
:group_name => a[:name],
:from_port => perm[:from_port].to_i,
:to_port => perm[:to_port].to_i,
:cidr_ip => range,
:ip_protocol => perm[:protocol]
}
end.flatten
end
end.flatten
}.flatten.uniq
# p ['running', name, 'with', authorizes.map{|a| a.to_hash}, 'currently', current_security_groups]
authorizes_requested = authorizes.select{|a| a.name.to_s == name.to_s }
authorizes_requested_hashes = authorizes_requested.map{|a| a.to_hash}
authorizes_needed = []
# take each requested authorization. If it doesn't exist in the current_security_groups, we need to add it.
authorizes_requested.each do |a|
next if current_security_groups.include?(a.to_hash)
p ['authorize needed:', name, a.to_hash]
authorizes_needed << a
end
# conversely, every current_security_groups authorization that isn't in the authorizes_requested list must be revoked
current_security_groups.each do |hsh|
next if authorizes_requested_hashes.include?(hsh)
if name == 'default'
p ['Not revoking, but please check that this security opening is valid:', name, hsh]
else
p ['revoke needed:', name, hsh]
revoke(hsh.merge(:protocol => hsh[:ip_protocol]))
end
end
maybe 'revoke security group settings' do
revokes.each{|r| r.run }
end unless revokes.blank?
if not authorizes_needed.blank?
authorizes_needed.each{|a| a.run}
end
end
def authorize(o={}, &block)
authorizes << Authorize.new("#{name}", o.merge(:parent => parent, :cloud => cloud), &block)
end
def revoke(o={}, &block)
revokes << Revoke.new("#{name}", o.merge(:parent => parent, :cloud => cloud), &block)
end
def create_security_group!
$stderr.puts "creating security group #{name}"
ec2.create_security_group(:group_name => name, :group_description => "PoolParty generated security group: #{name}")
end
def should_create_security_group?
security_groups.empty?
end
def security_groups
@security_groups ||= all_security_groups.select{|sg| sg[:name] == name }
end
def all_security_groups
@all_security_groups ||= self.class.all_security_groups(ec2)
end
def self.all_security_groups(ec2)
@all_security_groups ||= ec2.describe_security_groups.securityGroupInfo.item.map do |sg|
perms = sg["ipPermissions"] || {"item" => []} rescue [{"item" => []}]
{
:name => sg["groupName"],
:description => sg["groupDescription"],
:ip_permissions => perms["item"].map do |i|
ip_ranges = i["ipRanges"] || {"item" => []} rescue {"item" => []}
hsh = {
:protocol => i["ipProtocol"],
:from_port => i["fromPort"],
:to_port => i["toPort"],
:ip_ranges => ip_ranges["item"].map do |ip|
{
:cidr_ip => ip["cidrIp"]
}
end
}
if i["groups"].blank?
hsh
else
i["groups"]["item"].map{|grp| hsh.merge :group_name => grp["groupName"] }
end
end.flatten
}
end
end
def to_s
name.to_s
end
def authorizes
@authorizes ||= []
end
def revokes
@revokes ||= []
end
end
end
================================================
FILE: lib/cloud_providers/remote_instance.rb
================================================
=begin rdoc
Remote instances
=end
module CloudProviders
class RemoteInstance
include Dslify, Connections
attr_reader :name, :init_opts, :raw_response
attr_accessor :cloud_provider
default_options(
:instance_id => nil,
:image_id => nil,
:status => nil
)
def initialize(init_opts={}, &block)
@init_opts = init_opts
set_vars_from_options(init_opts)
instance_eval &block if block
after_initialized
end
def keypair(n=nil)
@keypair ||= n.nil? ? nil : Keypair.new(n)
end
def after_initialized
end
def accessible?
ping_port(public_ip, 22, 40)
end
def rsync_dir(dir)
rsync :source => dir/"*", :destination => "/"
end
def chef_bootstrapped?
@chef_bootstrapped ||= cloud.chef.node_bootstrapped?(self)
end
# TODO: chef_bootstrapped? should go away, since Chef#node_bootstrap!
# already checks node_bootstrap!. There is a force flag as well.
def bootstrap_chef!
cloud.chef.node_bootstrap!(self)
end
def run_chef!
cloud.chef.node_run!(self)
end
def run
warn "#{self.class} does not implement run. Something is wrong"
end
def default_keypair_path
self.class.default_keypair_path
end
## provide hash like methods to access and iterate over node attributes
def each
dsl_options.each{ |k,v| yield k,v }
end
def [](k)
if dsl_options.has_key? k
dsl_options[k]
else
nil
end
end
def []=(k,v)
dsl_options[k] = v
end
def has_key?(key)
dsl_options.has_key?(key)
end
def keys
dsl_options.keys
end
def values
dsl_options.values
end
def to_hash
dsl_options
end
##end of hash like methods
# Is this instance running?
def running?
!(status =~ /running/).nil?
end
# Is this instance pending?
def pending?
!(status =~ /pending/).nil?
end
# Is this instance terminating?
def terminating?
!(status =~ /shutting/).nil?
end
# Has this instance been terminated?
def terminated?
!(status =~ /terminated/).nil?
end
# elapsed seconds since node launch time
def elapsed_runtime
Time.now - Time.parse(launch_time)
end
def ssh_available?
warn "Implemented in cloudprovider instance class. something is wrong"
end
# def to_s
# (cloud ? to_hash.merge(:cloud=>cloud.name) : to_hash)
# end
private
def cloud
init_opts.has_key?(:cloud) ? init_opts[:cloud] : nil
end
def cloud_provider
cloud.cloud_provider
end
end
end
================================================
FILE: lib/cloud_providers.rb
================================================
=begin rdoc
CloudProvider is the base class for cloud computing services such as Ec2, Eucalyptus - where your servers run.
=end
module CloudProviders
# List of all defined cloud_providers
def self.all
@all ||= []
end
end
%w(connections remote_instance cloud_provider).each do |lib|
require File.dirname(__FILE__)+"/cloud_providers/#{lib}"
end
%w(ec2).each do |lib|
require "cloud_providers/#{lib}/#{lib}"
end
================================================
FILE: lib/core/array.rb
================================================
=begin rdoc
Array extensions
=end
class Array
# Example nodes.select_with_hash(:status=>'running')
def select_with_hash(conditions={})
return self if conditions.empty?
select do |node|
conditions.any? do |k,v|
( node.has_key?(k) && node[k]==v ) or ( node.respond_to?(k) && node.send(k)==v )
end
end
end
end
================================================
FILE: lib/core/hash.rb
================================================
=begin rdoc
Hash extentions
=end
class Hash
# Return a hash of all the elements where the block evaluates to true
def choose(&block)
Hash[*self.select(&block).inject([]){|res,(k,v)| res << k << v}]
end
# Computes the difference between two hashes
def diff(other, *hsh)
o = {}
keys.map do |k|
if hsh.include?(k) || hsh.empty?
other[k] == self[k] ? nil : o.merge!({k => other[k]})
end
end.reject {|b| b.nil? }
o
end
def merge_if!(k, v)
self[k] = v if v
self
end
# Converts all of the keys to strings
# can pass in a :key_modifier that will be sent to each key, before being symbolized.
# This can be usefull if you want to downcase, or snake_case each key.
# >> {'Placement' => {'AvailabilityZone'=>"us-east-1a"} }.symbolize_keys(:snake_case)
# => {:placement=>{:availability_zone=>"us-east-1a"}}
def symbolize_keys!(key_modifier=nil)
keys.each{|k|
v = delete(k)
if key_modifier && k.respond_to?(key_modifier)
k = k.send(key_modifier)
end
self[k.to_sym] = v
v.symbolize_keys!(key_modifier) if v.is_a?(Hash)
v.each{|p| p.symbolize_keys!(key_modifier) if p.is_a?(Hash)} if v.is_a?(Array)
}
self
end
def method_missing(sym, *args, &block)
if has_key?(sym.to_sym)
fetch(sym)
elsif has_key?(sym.to_s)
fetch(sym.to_s)
else
super
end
end
end
================================================
FILE: lib/core/object.rb
================================================
class Object
def pool(name=nil, &block)
@@pool ||= PoolParty::Pool.new(name, &block)
end
def reset!
@@pool = nil
end
def print_msg(msg_arr)
msg_arr.each do |line|
puts line
end
end
# return true if nil?, empty? or size==0
def blank?
return true if nil?
return true if self.respond_to?('empty?') && self.empty?
return true if self.respond_to?('size') && self.size==0
return false
end
# === Description
#
# Change +attr+ to +val+ within the scope of block
# and then sets it back again
#
def change_attr attr, val, &block
old_val = instance_variable_get attr
begin
instance_variable_set attr, val
yield
ensure
instance_variable_set attr, old_val
end
end
def progress_bar_until(msg=nil, &block)
print "#{msg}" if msg
loop do
if block.call
break
else
$stdout.print "."
$stdout.flush
sleep 1
end
end
print " OK" if msg
puts "" if msg
end
end
================================================
FILE: lib/core/string.rb
================================================
class String
# Turn a downcased string and capitalize it
# so that it can be a class
# doc_river #=> DocRiver
def camelcase
gsub(/(^|_|-)(.)/) { $2.upcase }
end
# "FooBar".snake_case #=> "foo_bar"
def snake_case
gsub(/\B[A-Z]+/, '_\&').downcase
end
# "FooBar".dasherize #=> "foo-bar"
def dasherize
gsub(/\B[A-Z]+/, '-\&').downcase
end
# Turn a string from lowercased with a .
# to a classified classname
# rice_and_beans #=> "RiceAndBeans"
# handles subclassed and namespaced classes as well
# for instance
# rice::and::beans #=> Rice::And::Beans
def classify
self.sub(/.*\./, '').split("::").map {|ele| ele.camelcase }.join("::")
end
# Constantize tries to find a declared constant with the name specified
# in the string. It raises a NameError when the name is not in CamelCase
# or is not initialized.
#
# Examples
# "Module".constantize #=> Module
# "Class".constantize #=> Class
def constantize(mod=Object)
camelcased_word = classify
begin
mod.module_eval(camelcased_word, __FILE__, __LINE__)
rescue NameError
nil
end
end
def /(o)
File.join(self, o.to_s)
end
end
================================================
FILE: lib/core/symbol.rb
================================================
class Symbol
# def >(num);"#{self} > #{num}";end
# def <(num);"#{self} < #{num}";end
# def >=(num);"#{self} >= #{num}";end
# def <=(num);"#{self} <= #{num}";end
# def ==(num);"#{self} > #{num}";end
def sanitize
self.to_s.sanitize
end
def <=>(b)
"#{self}" <=> "#{b}"
end
##
# @param o The path component to join with the string.
#
# @return The original path concatenated with o.
#
# @example
# :merb/"core_ext" #=> "merb/core_ext"
def /(o)
File.join(self.to_s, o.to_s)
end
# Classify the symbol
def classify
to_s.classify.to_sym
end
end
================================================
FILE: lib/keypair.rb
================================================
=begin rdoc
ssh key used to login to remote instances\
=end
class Keypair
include SearchablePaths
has_searchable_paths(:prepend_paths => [Dir.pwd, '/etc/poolparty/keys', "#{ENV["HOME"]}/.ssh/", "#{ENV["HOME"]}/.ec2/", ENV['EC2_CONFIG_DIR']])
# Amazon will not append suffix, but public key may have '.pem' suffix
SEARCH_SUFFIXES = %w( .pem )
attr_accessor :filepath
attr_reader :extra_paths, :opts
attr_reader :search_suffixes
# Create a new key that defaults to id_rsa as the name.
def initialize(fpath, extra_paths=[], opts={})
@filepath = fpath
@opts = opts
@extra_paths = [extra_paths].flatten.map {|a| File.expand_path(a) }
@search_suffixes = SEARCH_SUFFIXES
end
# If the full_filepath is nil or false, then the key doesn't exist
def exists?
!! full_filepath
end
# Read the content of the key
def content
@content ||= exists? ? open(full_filepath).read : nil
end
# Returns the full_filepath of the key. If a full filepath is passed, we just return the expanded filepath
# for the keypair, otherwise query where it is against known locations
def full_filepath
@full_filepath ||=
find_file_in_path_with_suffix(filepath, extra_paths,
search_suffixes) || false
end
def to_s
basename
end
#TODO: gracefully handle the case when a passpharase is needed
# Generate a public key from the private key
# net/ssh already has this built-in from our extension.
def public_key
if !@public_key_string || @public_key_string.empty?
pkey = Net::SSH::KeyFactory.load_private_key(full_filepath)
@public_key_string = pkey.public_key
else
@public_key_string
end
end
def public_key=(str)
@public_key_string = str
end
# Basename of the keypair
def basename
@basename ||= ::File.basename(filepath, ::File.extname(filepath))
end
# Just the filename of the keypair
def filename
@filename ||= ::File.basename(full_filepath) rescue filepath
end
# Support to add the enumerable each to keys
def each
yield full_filepath
end
# Validation checks
# if all of the validations pass, the object is considered valid
# the validations are responsible for raising a PoolPartyError (StandardError)
def valid?
validations.each {|validation| self.send(validation.to_sym) }
end
private
# Validations
def validations
[:keypair_found?, :has_proper_permissions?]
end
# Check the proper permissions
def has_proper_permissions?
perm_truth = [:readable?, :writable?, :executable?].map {|meth| File.send(meth, full_filepath)} == [true, true, false]
raise StandardError.new("Your keypair #{full_filepath} has improper file permissions. Keypairs must be 0600 permission. Please chmod your keypair file and try again") unless perm_truth
end
def keypair_found?
if exists?
true
else
raise StandardError.new("#{filepath} key file cannot be found") unless filepath.nil?
end
end
# try filename with suffix and without suffixes.
# Checks all paths without suffix first, then try all paths for all suffixes.
def find_file_in_path_with_suffix(file, extra_paths, suffixes=[],
try_wo_suffix=true)
suffixes_to_try = suffixes.dup
suffixes_to_try.push '' if try_wo_suffix
suffixes_to_try.map {|s| file + s }.each do |suffixed|
fullpath = if File.file?(File.expand_path(suffixed))
::File.expand_path(suffixed)
else
search_in_known_locations(suffixed, extra_paths)
end
return fullpath if fullpath
end
nil
end
end
================================================
FILE: lib/poolparty/base.rb
================================================
=begin rdoc
Base class for all PoolParty objects
=end
module PoolParty
class Base
include Dslify
attr_reader :name
def initialize(name, o={}, &block)
@name = name
@init_opts = o
set_vars_from_options(o)
instance_eval &block if block
after_initialized
end
def after_initialized
end
def run
warn "#{self.class} does not implement run. Something is wrong"
end
def method_missing(m,*a,&block)
if parent.respond_to?(m)
parent.send(m,*a,&block)
else
super
end
end
private
end
end
================================================
FILE: lib/poolparty/chef.rb
================================================
module PoolParty
class Chef < Base
BOOTSTRAP_PACKAGES = %w( ruby ruby1.8-dev libopenssl-ruby1.8 rdoc
ri irb build-essential wget ssl-cert rubygems git-core rake
librspec-ruby libxml-ruby zlib1g-dev libxml2-dev )
# thin couchdb
BOOTSTRAP_GEMS = %w( chef )
# we dont specifically install these binaries, they installed by
# packages and gems above, but we check for them
BOOTSTRAP_BINS = %w( gem chef-solo chef-client )
BOOTSTRAP_DIRS = %w( /var/log/chef /var/cache/chef /var/run/chef )
def compile!
build_tmp_dir
end
def self.types
return [:solo,:client]
end
def self.get_chef(type,cloud,&block)
("Chef" + type.to_s.capitalize).constantize(PoolParty).send(:new,type,:cloud => cloud,&block)
end
# Chef
def attributes(hsh={}, &block)
@attributes ||= ChefAttribute.new(hsh, &block)
end
def override_attributes(hsh={}, &block)
@override_attributes ||= ChefAttribute.new(hsh, &block)
end
# === Description
#
# Provides the ability to specify steps that can be
# run via chef
#
# pool "mycluster" do
# cloud "mycloud" do
#
# on_step :download_install do
# recipe "myrecipes::download"
# recipe "myrecipes::install"
# end
#
# on_step :run => :download_install do
# recipe "myrecipes::run"
# end
# end
# end
#
# Then from the command line you can do
#
# cloud-configure --step=download_install
#
# to only do the partial job or
#
# cloud-configure --step=run
#
# to do everything
#
def on_step action, &block
if action.is_a? Hash
t = action
action = t.keys[0]
depends = t.values[0]
else
depends = nil
end
change_attr :@_current_action, action do
yield
if depends
# Merge the recipes of the dependency into
# the current recipes
_recipes(depends).each do |r|
recipe r
end
end
end
end
# Adds a chef recipe to the cloud
#
# The hsh parameter is inserted into the override_attributes.
# The insertion is performed as follows. If
# the recipe name = "foo::bar" then effectively the call is
#
# override_attributes.merge! { :foo => { :bar => hsh } }
def recipe(recipe_name, hsh={})
_recipes << recipe_name unless _recipes.include?(recipe_name)
head = {}
tail = head
recipe_name.split("::").each do |key|
unless key == "default"
n = {}
tail[key] = n
tail = n
end
end
tail.replace hsh
override_attributes.merge!(head) unless hsh.empty?
end
def recipes(*recipes)
recipes.each do |r|
recipe(r)
end
end
def node_run!(remote_instance)
node_stop!(remote_instance)
node_configure!(remote_instance)
envhash = {
:GEM_BIN => %q%$(gem env | grep "EXECUTABLE DIRECTORY" | awk "{print \\$4}")%
}
cmds = chef_cmd
cmds = [cmds] unless cmds.respond_to? :each
remote_instance.ssh(cmds.map{|c| c.strip.squeeze(' ')}, :env => envhash )
end
def node_stop!(remote_instance)
remote_instance.ssh("killall -q chef-client chef-solo; [ -f /etc/init.d/chef-client ] && invoke-rc.d chef-client stop")
end
def node_configure!(remote_instance)
# nothing in the superclass
end
def node_bootstrapped?(remote_instance, quiet=true)
# using which command instead of calling gem directly. On
# ubuntu, calling a command from package not installed
# 'helpfully' prints message, which result confuses detection
#
cmd = "which %s" % BOOTSTRAP_BINS.join(' ') +
" && dpkg -l %s " % BOOTSTRAP_PACKAGES.join(' ') +
BOOTSTRAP_GEMS.map{ |gem|
"&& gem search '^#{gem}$' | grep -v GEMS | wc -l | grep -q 1"
}.join(' ') +
BOOTSTRAP_DIRS.map{ |dir|
"&& [[ -d #{dir} ]] "
}.join(' ') +
(quiet ? " >/dev/null " : "" ) +
" && echo OK || echo MISSING"
r = remote_instance.ssh(cmd, :do_sudo => false )
r.split("\n").to_a.last.chomp == "OK"
end
def node_bootstrap!(remote_instance, force=false)
return if !force && node_bootstrapped?(remote_instance)
# TODO: this should not be hardcoded (like in node_run)
deb_gem_bin='/var/lib/gems/1.8/bin'
gem_src='http://gems.opscode.com'
bootstrap_cmds =
[
'apt-get update',
'apt-get autoremove -y',
'apt-get install -y %s' % BOOTSTRAP_PACKAGES.join(' '),
"gem source -l | grep -q #{gem_src} || gem source -a #{gem_src} ",
'gem install %s --no-rdoc --no-ri' %
(BOOTSTRAP_GEMS + remote_instance.bootstrap_gems).join(' '),
"apt-get install -y %s" % BOOTSTRAP_PACKAGES.join(' '),
"[ -d #{deb_gem_bin} ] && ln -sf #{deb_gem_bin}/* /usr/local/bin",
"mkdir -p %s" % BOOTSTRAP_DIRS.join(' ')
]
remote_instance.ssh(bootstrap_cmds)
end
def _recipes action = nil
action = action.to_sym unless action.nil?
@_recipes ||= {:default => [] }
key = action || _current_action
@_recipes[key] ||= []
end
private
def _current_action
@_current_action ||= :default
end
def chef_cmd
if ENV["CHEF_DEBUG"]
debug = "-l debug"
else
debug = ""
end
return <<-CMD
PATH="$PATH:$GEM_BIN" #{chef_bin} -j /etc/chef/dna.json -c /etc/chef/client.rb -d -i 1800 -s 20 #{debug}
CMD
end
def method_missing(m,*args,&block)
if cloud.respond_to?(m)
cloud.send(m,*args,&block)
else
super
end
end
end
end
================================================
FILE: lib/poolparty/chef_attribute.rb
================================================
module PoolParty
class ChefAttribute < Base
attr_reader :init_opts
def initialize(opts={}, &block)
@init_block = block
@init_opts = opts
instance_eval &block if block
@base_name = self.name
end
def merge!(h={})
init_opts.merge!(h)
end
def to_dna(recipes, filepath, opts=init_opts)
if recipes && !recipes.empty?
(opts[:recipes] ||= []) << recipes
opts[:recipes].flatten!
end
opts.delete(:name) if opts[:name] && opts[:name].empty?
File.open(filepath, "w") do |f|
f << JSON.pretty_generate(opts)
end
end
def method_missing(m,*a,&block)
if @init_opts.has_key?(m)
@init_opts[m]
else
@init_opts.merge!(m => a)
end
end
end
end
================================================
FILE: lib/poolparty/chef_client.rb
================================================
require 'uri' # for URI.parse in write_bootstrap_files
module PoolParty
# Chef class bootstrapping chef-client.
class ChefClient < Chef
dsl_methods :server_url,:validation_token, :validation_key, :validation_client_name
# When init_style.nil?, old behavior is used (just run the client).
# If init_style is specified, bootstrap::client cookbook is executed
# To this init style.
dsl_methods :init_style
def openid_url(url=nil)
if url.nil?
return @openid_url||= (u=URI.parse(server_url)
u.port=4001
openid_url u.to_s)
else
@openid_url=url
end
end
def roles(*roles)
return @_roles||=[cloud.name] if roles.empty?
@_roles=roles
end
private
def after_initialized
raise PoolPartyError.create("ChefArgumentMissing", "server_url must be specified!") unless server_url
end
def chef_bin
"chef-client"
end
def chef_cmd
# without init_style, let parent class start chef-client
return super unless init_style
'invoke-rc.d chef-client start'
end
def node_configure!(remote_instance)
super
cmds =
[ 'PATH="$PATH:$GEM_BIN" chef-solo -j /tmp/chef/chef.json -c /tmp/chef/solo.rb',
'invoke-rc.d chef-client stop',
'PATH="$PATH:$GEM_BIN" chef-client -j /etc/chef/dna.json -c /etc/chef/client.rb',
]
remote_instance.ssh cmds
end
# The NEW actual chef resolver.
def build_tmp_dir
base_directory = tmp_path/"etc"/"chef"
FileUtils.rm_rf base_directory
FileUtils.mkdir_p base_directory
FileUtils.cp validation_key, base_directory if validation_key
puts "Creating the dna.json"
attributes.to_dna [], base_directory/"dna.json", {:run_list => roles.map{|r| "role[#{r}]"} + _recipes.map{|r| "recipe[#{r}]"}}.merge(attributes.init_opts)
unless init_style then # original style init
write_client_dot_rb
else
bootstrap_tmp_dir = tmp_path/"tmp/chef"
FileUtils.rm_rf bootstrap_tmp_dir
FileUtils.mkdir_p bootstrap_tmp_dir
write_bootstrap_files bootstrap_tmp_dir/"solo.rb", bootstrap_tmp_dir/"chef.json"
end
end
def write_client_dot_rb(to=tmp_path/"etc"/"chef"/"client.rb")
content = <<-EOE
log_level :info
log_location "/var/log/chef/client.log"
ssl_verify_mode :verify_none
file_cache_path "/var/cache/chef"
pid_file "/var/run/chef/client.pid"
Chef::Log::Formatter.show_time = true
openid_url "#{openid_url}"
EOE
%w(chef_server_url).each{|url|
content+="#{url} \"#{server_url}\"\n"
}
content+="validation_token \"#{validation_token}\"\n" if validation_token
content+="validation_key \"/etc/chef/#{File.basename validation_key}\"\n" if validation_key
content+="validation_client_name \"#{validation_client_name}\"\n" if validation_client_name
File.open(to, "w") do |f|
f << content
end
end
def write_bootstrap_files(solo_rb, chef_json)
uri=URI.parse(server_url)
# this maybe reduntant, URL should have a port in there
uri.port=4000 if uri.port == 80 # default port for chef
contents_solo_rb = <<-EOE
file_cache_path "/tmp/chef-solo"
cookbook_path "/tmp/chef-solo/cookbooks"
recipe_url "http://s3.amazonaws.com/chef-solo/bootstrap-latest.tar.gz"
EOE
File.open(solo_rb, "w") do |f| f << contents_solo_rb end
bootstrap_json =
{
:bootstrap => {
:chef => {
:url_type => uri.scheme,
:init_style => init_style,
:path => "/srv/chef",
:serve_path => "/srv/chef",
:server_fqdn => uri.host + uri.path,
:server_port => uri.port,
},
},
:run_list => [ 'recipe[bootstrap::client]' ],
}
if validation_client_name
bootstrap_json[:bootstrap][:chef][:validation_client_name] = validation_client_name
end
ChefAttribute.new(bootstrap_json).to_dna([], chef_json)
end
end
end
================================================
FILE: lib/poolparty/chef_solo.rb
================================================
require "fileutils"
module PoolParty
class ChefSolo < Chef
dsl_methods :repo
private
def chef_bin
"chef-solo"
end
def chef_cmd
if ENV["CHEF_DEBUG"]
debug = "-l debug"
else
debug = ""
end
return <<-CMD
PATH="$PATH:$GEM_BIN" #{chef_bin} -j /etc/chef/dna.json -c /etc/chef/solo.rb #{debug}
CMD
end
# The NEW actual chef resolver.
def build_tmp_dir
base_directory = tmp_path/"etc"/"chef"
roles_dir = "#{base_directory}/roles"
FileUtils.rm_rf base_directory # cleanup old chef temp directory
puts "Copying the chef-repo into the base directory from #{repo}"
FileUtils.mkdir_p base_directory
FileUtils.mkdir_p roles_dir # Why do we need this??!?
if File.directory?(repo)
if File.exist?(base_directory)
# First remove the directory
FileUtils.remove_entry base_directory, :force => true
end
cookbook_path = "#{base_directory}/cookbooks"
FileUtils.mkdir_p cookbook_path
FileUtils.cp_r "#{repo}/.", cookbook_path
else
raise "#{repo} chef repo directory does not exist"
end
puts "Creating the dna.json"
attributes.to_dna [], base_directory/"dna.json", {:run_list => ["role[#{cloud.name}]"]}
write_solo_dot_rb
# Make sure the roles directory exists
FileUtils.mkdir_p roles_dir
write_chef_role_json "#{roles_dir}/#{cloud.name}.json"
end
def write_solo_dot_rb(to=tmp_path/"etc"/"chef"/"solo.rb")
content = <<-EOE
cookbook_path ["/etc/chef/cookbooks/cookbooks", "/etc/chef/cookbooks/site-cookbooks"]
role_path "/etc/chef/roles"
log_level :info
EOE
File.open(to, "w") do |f|
f << content
end
end
def write_chef_role_json(to=tmp_path/"etc"/"chef"/"dna.json")
# Add the parent name and the name of the cloud to
# the role for easy access in recipes.
pp = {
:poolparty => {
:parent_name => cloud.parent.name,
:name => cloud.name,
:pool_info => pool.to_hash
}
}
override_attributes.merge! pp
ca = ChefAttribute.new({
:name => cloud.name,
:json_class => "Chef::Role",
:chef_type => "role",
:default_attributes => attributes.init_opts,
:override_attributes => override_attributes.init_opts,
:description => description
})
ca.to_dna _recipes(pool.chef_step).map {|a| File.basename(a) }, to
end
end
end
================================================
FILE: lib/poolparty/cloud.rb
================================================
module PoolParty
class Cloud < Base
default_options(
:description => "PoolParty cloud",
:minimum_instances => 1,
:maximum_instances => 3
)
# returns an instance of Keypair
# You can pass either a filename which will be searched for in ~/.ec2/ and ~/.ssh/
# Or you can pass a full filepath
def keypair(n=nil, extra_paths=[])
return @keypair if @keypair
@keypair = case n
when String
Keypair.new(n, extra_paths)
when nil
fpath = CloudProviders::CloudProvider.default_keypair_path/"#{proper_name}"
File.exists?(fpath) ? Keypair.new(fpath, extra_paths) : generate_keypair(extra_paths)
else
raise ArgumentError, "There was an error when defining the keypair"
end
end
private
def generate_keypair(extra_paths=[])
puts "Generate the keypair for this cloud because its not found: #{proper_name}"
cloud_provider.send :generate_keypair, proper_name
Keypair.new(proper_name, extra_paths)
end
def after_initialized
raise PoolParty::PoolPartyError.create("NoCloudProvider", <<-EOE
You did not specify a cloud provider in your clouds.rb. Make sure you have a block that looks like:
using :ec2
EOE
) unless cloud_provider
security_group(proper_name, :authorize => {:from_port => 22, :to_port => 22}) if security_groups.empty?
end
public
def instances(arg)
case arg
when Range
minimum_instances arg.first
maximum_instances arg.last
when Fixnum
minimum_instances arg
maximum_instances arg
when Hash
minimum_instances arg[:instances].to_i
maximum_instances arg[:instances].to_i
# nodes(arg)
else
raise PoolParty::PoolPartyError.create("DslMethodCall", "You must call instances with either a number, a range or a hash (for a list of nodes)")
end
end
# Upload the source to dest ( using rsync )
def upload source, dest
@uploads ||= []
@uploads << { :source => source, :dest => dest }
end
# The pool can either be the parent (the context where the object is declared)
# or the global pool object
def pool
parent || pool
end
def tmp_path
"/tmp/poolparty" / pool.name / name
end
public
attr_reader :cloud_provider
def using(provider_name, &block)
return @cloud_provider if @cloud_provider
@cloud_provider = "#{provider_name}".constantize(CloudProviders).send(:new, provider_name, :cloud => self, &block)
# Decorate the cloud with the cloud_provider methods
(class << self; self; end).instance_variable_set('@cloud_provider', @cloud_provider)
(class << self; self; end).class_eval do
@cloud_provider.public_methods(false).each do |meth|
next if respond_to?(meth) || method_defined?(meth) || private_method_defined?(meth)
eval <<-EOE
def #{meth}(*args, &block)
@cloud_provider.send(:#{meth}, *args, &block)
end
EOE
end
end
end
def chef(chef_type=:solo, &block)
raise ArgumentError, "Chef type must be one of #{Chef.types.map{|v| ":" + v.to_s}.join(",")}." unless Chef.types.include?(chef_type)
@chef||=Chef.get_chef(chef_type,self,&block)
end
# compile the cloud spec and execute the compiled system and remote calls
def run
puts " running on #{cloud_provider.class}"
cloud_provider.run
unless @chef.nil?
compile!
bootstrap!
end
end
# TODO: Incomplete and needs testing
# Shutdown and delete the load_balancers, auto_scaling_groups, launch_configurations,
# security_groups, triggers and instances defined by this cloud
def teardown
raise "Only Ec2 teardown supported" unless cloud_provider.name.to_s == 'ec2'
puts "! Tearing down cloud #{name}"
# load_balancers.each do |name, lb|
# puts "! Deleting load_balaner #{lb_name}"
# lb.teardown
# end
load_balancers.each do |lb|
puts "-----> Tearing down load balancer: #{lb.name}"
lb.teardown
end
rds_instances.each do |rds|
puts "-----> Tearing down RDS Instance: #{rds.name}"
rds.teardown
end
# instances belonging to an auto_scaling group must be deleted before the auto_scaling group
#THIS SCARES ME! nodes.each{|n| n.terminate_instance!}
# loop {nodes.size>0 ? sleep(4) : break }
if autoscalers.empty?
nodes.each do |node|
node.terminate!
end
else
autoscalers.each do |a|
puts "-----> Tearing down autoscaler #{a.name}"
a.teardown
end
end
# autoscalers.keys.each do |as_name|
# puts "! Deleting auto_scaling_group #{as_name}"
# cloud_provider.as.delete_autoscaling_group('AutoScalingGroupName' => as_name)
# end
#TODO: keypair.delete # Do we want to delete the keypair? probably, but not certain
end
def reboot!
orig_nodes = nodes
if autoscalers.empty?
puts <<-EOE
No autoscalers defined
Launching new nodes and then shutting down original nodes
EOE
# Terminate the nodes
orig_nodes.each_with_index do |node, i|
# Start new nodes
print "Starting node: #{i}...\n"
expand_by(1)
print "Terminating node: #{i}...\n"
node.terminate!
puts ""
end
else
# Terminate the nodes
@num_nodes = orig_nodes.size
orig_nodes.each do |node|
node.terminate!
puts "----> Terminated node: #{node.instance_id}"
# Wait for the autoscaler to boot the next node
puts "----> Waiting for new node to boot via the autoscaler"
loop do
reset!
break if nodes.size == @num_nodes
$stdout.print "."
$stdout.flush
sleep 1
end
end
end
run
puts ""
end
def compile!
unless @uploads.nil?
puts "Uploading files via rsync"
@uploads.each do |upload|
rsync upload[:source], upload[:dest]
end
end
@chef.compile! unless @chef.nil?
end
def bootstrap!
cloud_provider.bootstrap_nodes!(tmp_path)
end
def configure!
compile!
cloud_provider.configure_nodes!(tmp_path)
end
def reset!
cloud_provider.reset!
end
def ssh(num=0)
nodes[num].ssh
end
def rsync(source, dest)
nodes.each do |node|
node.rsync(:source => source, :destination => dest)
end
end
# TODO: list of nodes needs to be consistentley sorted
def nodes
cloud_provider.nodes.select {|a| a.in_service? }
end
# Run command/s on all nodes in the cloud.
# Returns a hash of instance_id=>result pairs
def cmd(commands, opts={})
key_by = opts.delete(:key_by) || :instance_id
results = {}
threads = nodes.collect do |n|
puts "result for #{n.instance_id} ==> n.ssh(#{commands.inspect}, #{opts.inspect})"
Thread.new{ results[ n.send(key_by) ] = n.ssh(commands, opts) }
end
threads.each{ |aThread| aThread.join }
results
end
def cssh(user = nil, use_keypair = nil)
opts = []
opts.push "-i #{keypair.full_filepath}" if use_keypair
opts.push "-l #{user}" if user
opts = opts.join(" ")
puts "cssh -o '#{opts}' " + nodes.map{|n| n.public_ip}.join(" ") + " &"
system "cssh -o '#{opts}' " + nodes.map{|n| n.public_ip}.join(" ") + " &"
end
# Explicit proxies to cloud_provider methods
def run_instance(o={}); cloud_provider.run_instance(o);end
def terminate_instance!(o={}); cloud_provider.terminate_instance!(o);end
def describe_instances(o={}); cloud_provider.describe_instances(o);end
def describe_instance(o={}); cloud_provider.describe_instance(o);end
def proper_name
"#{parent.name}-#{name}"
end
end
end
================================================
FILE: lib/poolparty/pool.rb
================================================
module PoolParty
class Pool < Base
attr_accessor :verbose, :very_verbose, :debugging, :very_debugging, :auto_execute
def cloud(name, &block)
c = Cloud.new(name.to_s, {:parent => self}, &block)
clouds[name.to_s] = c
# Create a dummy security group for tagging purposes. Do not want to
# conflict with advance usage of security groups
c.security_group "#poolparty-#{c.proper_name}"
end
def clouds
@clouds ||= {}
end
# Run command/s on all nodes in the pool.
# Returns a hash in the form of {cloud => [{instance_id=>result}]}
def cmd(commands, opts={})
results = {}
threads = clouds.collect do |name, c|
Thread.new{ results[ name ] = c.cmd(commands, opts) }
end
threads.each{ |aThread| aThread.join }
results
end
at_exit do
if pool.auto_execute
puts <<-EOE
----> Running #{pool.name} #{pool.auto_execute}
EOE
pool.run
end
end
# === Description
#
# Set / Get the chef_step which will be executed on the remote
# host
def chef_step name = nil
@selected_chef_step ||= :default
if name
@selected_chef_step = name.to_sym
end
@selected_chef_step
end
def run
clouds.each do |cloud_name, cld|
puts "----> Starting to build cloud #{cloud_name}"
cld.run
end
end
def to_hash
c = clouds.collect do |cloud_name, cld|
nodes = cld.nodes.collect do |node|
h = {}
[:dns_name, :private_ip, :public_ip].each do |f|
h[f] = node[f]
end
h
end
{ cloud_name => nodes }
end
h = c.inject({})do |old, new|
old.merge! new
end
{:clouds => h }
end
end
end
================================================
FILE: lib/poolparty/pool_party_error.rb
================================================
=begin rdoc
PoolPartyError
Create an StandardError on the fly
=end
module PoolParty
class PoolPartyError
# Create an error with the class_name and error message
# If the StandardError is not yet defined, define it, subclassing
# StandardError and return the new class
# Note: the class is set on Object
def self.create(class_name="StandardError", msg="Error")
Object.const_set(class_name, Class.new(StandardError)) unless Object.const_defined?(class_name)
class_name.constantize.send(:new, msg)
end
end
end
================================================
FILE: lib/poolparty/version.rb
================================================
module PoolParty
VERSION = "1.7.0.pre"
end
================================================
FILE: lib/poolparty.rb
================================================
$LOAD_PATH.unshift(File.dirname(__FILE__))
t=Time.now
require 'yaml'
# Load system gems
%w(rubygems logger erb open-uri fileutils timeout).each do |lib|
require lib
end
begin
require 'AWS'
rescue LoadError
puts <<-EOM
There was an error requiring AWS
EOM
end
require 'pp'
# Add all vendor gems to the load paths
Dir[File.dirname(__FILE__)+"/../vendor/gems/*"].each {|lib| $LOAD_PATH.unshift(File.expand_path("#{lib}/lib")) }
# Load local gems
%w(dslify json searchable_paths).each do |dep|
require dep
end
require "poolparty/version"
module PoolParty
def self.version
VERSION
end
def self.lib_dir
File.join(File.dirname(__FILE__), "..")
end
end
# Require the poolparty error so we can use it ubiquitously
require "poolparty/pool_party_error"
# Core object overloads
%w( object
string
array
hash
symbol
).each do |lib|
require "core/#{lib}"
end
require "keypair"
POOLPARTY_CONFIG_FILE = "#{ENV["HOME"]}/.poolparty/aws" unless defined?(POOLPARTY_CONFIG_FILE)
# PoolParty core
$LOAD_PATH.unshift(File.dirname(__FILE__)/"poolparty")
%w( base
chef_attribute
chef
chef_solo
chef_client
cloud pool
).each do |lib|
require "poolparty/#{lib}"
end
require 'cloud_providers'
puts "PoolParty core loadtime: #{Time.now-t}"
================================================
FILE: poolparty.gemspec
================================================
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
require "poolparty/version"
Gem::Specification.new do |s|
s.name = "poolparty"
s.version = PoolParty::VERSION
s.authors = ["Ari Lerner", "Michael Fairchild", "Nate Murray"]
s.email = %q{arilerner@mac.com}
s.homepage = %q{http://poolpartyrb.com}
s.summary = %q{Simple DSL to describe and realize cloud deployment architectures.}
s.description = %q{PoolParty: The easy, open-source, cross-cloud management solution}
s.extra_rdoc_files = [ "README.rdoc" ]
s.rdoc_options = ["--quiet", "--title", "PoolParty documentation", "--line-numbers", "--main", "README.rdoc"]
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
s.add_runtime_dependency("amazon-ec2", "~> 0.9.17")
s.add_runtime_dependency("xml-simple", ">= 0")
s.add_runtime_dependency("json", ">= 0")
end
================================================
FILE: setup.rb
================================================
#
# setup.rb
#
# Copyright (c) 2000-2005 Minero Aoki
#
# This program is free software.
# You can distribute/modify this program under the terms of
# the GNU LGPL, Lesser General Public License version 2.1.
#
unless Enumerable.method_defined?(:map) # Ruby 1.4.6
module Enumerable
alias map collect
end
end
unless File.respond_to?(:read) # Ruby 1.6
def File.read(fname)
open(fname) {|f|
return f.read
}
end
end
unless Errno.const_defined?(:ENOTEMPTY) # Windows?
module Errno
class ENOTEMPTY
# We do not raise this exception, implementation is not needed.
end
end
end
def File.binread(fname)
open(fname, 'rb') {|f|
return f.read
}
end
# for corrupted Windows' stat(2)
def File.dir?(path)
File.directory?((path[-1,1] == '/') ? path : path + '/')
end
class ConfigTable
include Enumerable
def initialize(rbconfig)
@rbconfig = rbconfig
@items = []
@table = {}
# options
@install_prefix = nil
@config_opt = nil
@verbose = true
@no_harm = false
end
attr_accessor :install_prefix
attr_accessor :config_opt
attr_writer :verbose
def verbose?
@verbose
end
attr_writer :no_harm
def no_harm?
@no_harm
end
def [](key)
lookup(key).resolve(self)
end
def []=(key, val)
lookup(key).set val
end
def names
@items.map {|i| i.name }
end
def each(&block)
@items.each(&block)
end
def key?(name)
@table.key?(name)
end
def lookup(name)
@table[name] or setup_rb_error "no such config item: #{name}"
end
def add(item)
@items.push item
@table[item.name] = item
end
def remove(name)
item = lookup(name)
@items.delete_if {|i| i.name == name }
@table.delete_if {|name, i| i.name == name }
item
end
def load_script(path, inst = nil)
if File.file?(path)
MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
end
end
def savefile
'.config'
end
def load_savefile
begin
File.foreach(savefile()) do |line|
k, v = *line.split(/=/, 2)
self[k] = v.strip
end
rescue Errno::ENOENT
setup_rb_error $!.message + "\n#{File.basename($0)} config first"
end
end
def save
@items.each {|i| i.value }
File.open(savefile(), 'w') {|f|
@items.each do |i|
f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
end
}
end
def load_standard_entries
standard_entries(@rbconfig).each do |ent|
add ent
end
end
def standard_entries(rbconfig)
c = rbconfig
rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
major = c['MAJOR'].to_i
minor = c['MINOR'].to_i
teeny = c['TEENY'].to_i
version = "#{major}.#{minor}"
# ruby ver. >= 1.4.4?
newpath_p = ((major >= 2) or
((major == 1) and
((minor >= 5) or
((minor == 4) and (teeny >= 4)))))
if c['rubylibdir']
# V > 1.6.3
libruby = "#{c['prefix']}/lib/ruby"
librubyver = c['rubylibdir']
librubyverarch = c['archdir']
siteruby = c['sitedir']
siterubyver = c['sitelibdir']
siterubyverarch = c['sitearchdir']
elsif newpath_p
# 1.4.4 <= V <= 1.6.3
libruby = "#{c['prefix']}/lib/ruby"
librubyver = "#{c['prefix']}/lib/ruby/#{version}"
librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
siteruby = c['sitedir']
siterubyver = "$siteruby/#{version}"
siterubyverarch = "$siterubyver/#{c['arch']}"
else
# V < 1.4.4
libruby = "#{c['prefix']}/lib/ruby"
librubyver = "#{c['prefix']}/lib/ruby/#{version}"
librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
siterubyver = siteruby
siterubyverarch = "$siterubyver/#{c['arch']}"
end
parameterize = lambda {|path|
path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
}
if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
else
makeprog = 'make'
end
[
ExecItem.new('installdirs', 'std/site/home',
'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
{|val, table|
case val
when 'std'
table['rbdir'] = '$librubyver'
table['sodir'] = '$librubyverarch'
when 'site'
table['rbdir'] = '$siterubyver'
table['sodir'] = '$siterubyverarch'
when 'home'
setup_rb_error '$HOME was not set' unless ENV['HOME']
table['prefix'] = ENV['HOME']
table['rbdir'] = '$libdir/ruby'
table['sodir'] = '$libdir/ruby'
end
},
PathItem.new('prefix', 'path', c['prefix'],
'path prefix of target environment'),
PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
'the directory for commands'),
PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
'the directory for libraries'),
PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
'the directory for shared data'),
PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
'the directory for man pages'),
PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
'the directory for system configuration files'),
PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
'the directory for local state data'),
PathItem.new('libruby', 'path', libruby,
'the directory for ruby libraries'),
PathItem.new('librubyver', 'path', librubyver,
'the directory for standard ruby libraries'),
PathItem.new('librubyverarch', 'path', librubyverarch,
'the directory for standard ruby extensions'),
PathItem.new('siteruby', 'path', siteruby,
'the directory for version-independent aux ruby libraries'),
PathItem.new('siterubyver', 'path', siterubyver,
'the directory for aux ruby libraries'),
PathItem.new('siterubyverarch', 'path', siterubyverarch,
'the directory for aux ruby binaries'),
PathItem.new('rbdir', 'path', '$siterubyver',
'the directory for ruby scripts'),
PathItem.new('sodir', 'path', '$siterubyverarch',
'the directory for ruby extentions'),
PathItem.new('rubypath', 'path', rubypath,
'the path to set to #! line'),
ProgramItem.new('rubyprog', 'name', rubypath,
'the ruby program using for installation'),
ProgramItem.new('makeprog', 'name', makeprog,
'the make program to compile ruby extentions'),
SelectItem.new('shebang', 'all/ruby/never', 'ruby',
'shebang line (#!) editing mode'),
BoolItem.new('without-ext', 'yes/no', 'no',
'does not compile/install ruby extentions')
]
end
private :standard_entries
def load_multipackage_entries
multipackage_entries().each do |ent|
add ent
end
end
def multipackage_entries
[
PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
'package names that you want to install'),
PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
'package names that you do not want to install')
]
end
private :multipackage_entries
ALIASES = {
'std-ruby' => 'librubyver',
'stdruby' => 'librubyver',
'rubylibdir' => 'librubyver',
'archdir' => 'librubyverarch',
'site-ruby-common' => 'siteruby', # For backward compatibility
'site-ruby' => 'siterubyver', # For backward compatibility
'bin-dir' => 'bindir',
'bin-dir' => 'bindir',
'rb-dir' => 'rbdir',
'so-dir' => 'sodir',
'data-dir' => 'datadir',
'ruby-path' => 'rubypath',
'ruby-prog' => 'rubyprog',
'ruby' => 'rubyprog',
'make-prog' => 'makeprog',
'make' => 'makeprog'
}
def fixup
ALIASES.each do |ali, name|
@table[ali] = @table[name]
end
@items.freeze
@table.freeze
@options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
end
def parse_opt(opt)
m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"
m.to_a[1,2]
end
def dllext
@rbconfig['DLEXT']
end
def value_config?(name)
lookup(name).value?
end
class Item
def initialize(name, template, default, desc)
@name = name.freeze
@template = template
@value = default
@default = default
@description = desc
end
attr_reader :name
attr_reader :description
attr_accessor :default
alias help_default default
def help_opt
"--#{@name}=#{@template}"
end
def value?
true
end
def value
@value
end
def resolve(table)
@value.gsub(%r<\$([^/]+)>) { table[$1] }
end
def set(val)
@value = check(val)
end
private
def check(val)
setup_rb_error "config: --#{name} requires argument" unless val
val
end
end
class BoolItem < Item
def config_type
'bool'
end
def help_opt
"--#{@name}"
end
private
def check(val)
return 'yes' unless val
case val
when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
when /\An(o)?\z/i, /\Af(alse)\z/i then 'no'
else
setup_rb_error "config: --#{@name} accepts only yes/no for argument"
end
end
end
class PathItem < Item
def config_type
'path'
end
private
def check(path)
setup_rb_error "config: --#{@name} requires argument" unless path
path[0,1] == '$' ? path : File.expand_path(path)
end
end
class ProgramItem < Item
def config_type
'program'
end
end
class SelectItem < Item
def initialize(name, selection, default, desc)
super
@ok = selection.split('/')
end
def config_type
'select'
end
private
def check(val)
unless @ok.include?(val.strip)
setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
end
val.strip
end
end
class ExecItem < Item
def initialize(name, selection, desc, &block)
super name, selection, nil, desc
@ok = selection.split('/')
@action = block
end
def config_type
'exec'
end
def value?
false
end
def resolve(table)
setup_rb_error "$#{name()} wrongly used as option value"
end
undef set
def evaluate(val, table)
v = val.strip.downcase
unless @ok.include?(v)
setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"
end
@action.call v, table
end
end
class PackageSelectionItem < Item
def initialize(name, template, default, help_default, desc)
super name, template, default, desc
@help_default = help_default
end
attr_reader :help_default
def config_type
'package'
end
private
def check(val)
unless File.dir?("packages/#{val}")
setup_rb_error "config: no such package: #{val}"
end
val
end
end
class MetaConfigEnvironment
def initialize(config, installer)
@config = config
@installer = installer
end
def config_names
@config.names
end
def config?(name)
@config.key?(name)
end
def bool_config?(name)
@config.lookup(name).config_type == 'bool'
end
def path_config?(name)
@config.lookup(name).config_type == 'path'
end
def value_config?(name)
@config.lookup(name).config_type != 'exec'
end
def add_config(item)
@config.add item
end
def add_bool_config(name, default, desc)
@config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
end
def add_path_config(name, default, desc)
@config.add PathItem.new(name, 'path', default, desc)
end
def set_config_default(name, default)
@config.lookup(name).default = default
end
def remove_config(name)
@config.remove(name)
end
# For only multipackage
def packages
raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
@installer.packages
end
# For only multipackage
def declare_packages(list)
raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
@installer.packages = list
end
end
end # class ConfigTable
# This module requires: #verbose?, #no_harm?
module FileOperations
def mkdir_p(dirname, prefix = nil)
dirname = prefix + File.expand_path(dirname) if prefix
$stderr.puts "mkdir -p #{dirname}" if verbose?
return if no_harm?
# Does not check '/', it's too abnormal.
dirs = File.expand_path(dirname).split(%r<(?=/)>)
if /\A[a-z]:\z/i =~ dirs[0]
disk = dirs.shift
dirs[0] = disk + dirs[0]
end
dirs.each_index do |idx|
path = dirs[0..idx].join('')
Dir.mkdir path unless File.dir?(path)
end
end
def rm_f(path)
$stderr.puts "rm -f #{path}" if verbose?
return if no_harm?
force_remove_file path
end
def rm_rf(path)
$stderr.puts "rm -rf #{path}" if verbose?
return if no_harm?
remove_tree path
end
def remove_tree(path)
if File.symlink?(path)
remove_file path
elsif File.dir?(path)
remove_tree0 path
else
force_remove_file path
end
end
def remove_tree0(path)
Dir.foreach(path) do |ent|
next if ent == '.'
next if ent == '..'
entpath = "#{path}/#{ent}"
if File.symlink?(entpath)
remove_file entpath
elsif File.dir?(entpath)
remove_tree0 entpath
else
force_remove_file entpath
end
end
begin
Dir.rmdir path
rescue Errno::ENOTEMPTY
# directory may not be empty
end
end
def move_file(src, dest)
force_remove_file dest
begin
File.rename src, dest
rescue
File.open(dest, 'wb') {|f|
f.write File.binread(src)
}
File.chmod File.stat(src).mode, dest
File.unlink src
end
end
def force_remove_file(path)
begin
remove_file path
rescue
end
end
def remove_file(path)
File.chmod 0777, path
File.unlink path
end
def install(from, dest, mode, prefix = nil)
$stderr.puts "install #{from} #{dest}" if verbose?
return if no_harm?
realdest = prefix ? prefix + File.expand_path(dest) : dest
realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
str = File.binread(from)
if diff?(str, realdest)
verbose_off {
rm_f realdest if File.exist?(realdest)
}
File.open(realdest, 'wb') {|f|
f.write str
}
File.chmod mode, realdest
File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
if prefix
f.puts realdest.sub(prefix, '')
else
f.puts realdest
end
}
end
end
def diff?(new_content, path)
return true unless File.exist?(path)
new_content != File.binread(path)
end
def command(*args)
$stderr.puts args.join(' ') if verbose?
system(*args) or raise RuntimeError,
"system(#{args.map{|a| a.inspect }.join(' ')}) failed"
end
def ruby(*args)
command config('rubyprog'), *args
end
def make(task = nil)
command(*[config('makeprog'), task].compact)
end
def extdir?(dir)
File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
end
def files_of(dir)
Dir.open(dir) {|d|
return d.select {|ent| File.file?("#{dir}/#{ent}") }
}
end
DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
def directories_of(dir)
Dir.open(dir) {|d|
return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
}
end
end
# This module requires: #srcdir_root, #objdir_root, #relpath
module HookScriptAPI
def get_config(key)
@config[key]
end
alias config get_config
# obsolete: use metaconfig to change configuration
def set_config(key, val)
@config[key] = val
end
#
# srcdir/objdir (works only in the package directory)
#
def curr_srcdir
"#{srcdir_root()}/#{relpath()}"
end
def curr_objdir
"#{objdir_root()}/#{relpath()}"
end
def srcfile(path)
"#{curr_srcdir()}/#{path}"
end
def srcexist?(path)
File.exist?(srcfile(path))
end
def srcdirectory?(path)
File.dir?(srcfile(path))
end
def srcfile?(path)
File.file?(srcfile(path))
end
def srcentries(path = '.')
Dir.open("#{curr_srcdir()}/#{path}") {|d|
return d.to_a - %w(. ..)
}
end
def srcfiles(path = '.')
srcentries(path).select {|fname|
File.file?(File.join(curr_srcdir(), path, fname))
}
end
def srcdirectories(path = '.')
srcentries(path).select {|fname|
File.dir?(File.join(curr_srcdir(), path, fname))
}
end
end
class ToplevelInstaller
Version = '3.4.1'
Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
TASKS = [
[ 'all', 'do config, setup, then install' ],
[ 'config', 'saves your configurations' ],
[ 'show', 'shows current configuration' ],
[ 'setup', 'compiles ruby extentions and others' ],
[ 'install', 'installs files' ],
[ 'test', 'run all tests in test/' ],
[ 'clean', "does `make clean' for each extention" ],
[ 'distclean',"does `make distclean' for each extention" ]
]
def ToplevelInstaller.invoke
config = ConfigTable.new(load_rbconfig())
config.load_standard_entries
config.load_multipackage_entries if multipackage?
config.fixup
klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
klass.new(File.dirname($0), config).invoke
end
def ToplevelInstaller.multipackage?
File.dir?(File.dirname($0) + '/packages')
end
def ToplevelInstaller.load_rbconfig
if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
ARGV.delete(arg)
load File.expand_path(arg.split(/=/, 2)[1])
$".push 'rbconfig.rb'
else
require 'rbconfig'
end
::Config::CONFIG
end
def initialize(ardir_root, config)
@ardir = File.expand_path(ardir_root)
@config = config
# cache
@valid_task_re = nil
end
def config(key)
@config[key]
end
def inspect
"#<#{self.class} #{__id__()}>"
end
def invoke
run_metaconfigs
case task = parsearg_global()
when nil, 'all'
parsearg_config
init_installers
exec_config
exec_setup
exec_install
else
case task
when 'config', 'test'
;
when 'clean', 'distclean'
@config.load_savefile if File.exist?(@config.savefile)
else
@config.load_savefile
end
__send__ "parsearg_#{task}"
init_installers
__send__ "exec_#{task}"
end
end
def run_metaconfigs
@config.load_script "#{@ardir}/metaconfig"
end
def init_installers
@installer = Installer.new(@config, @ardir, File.expand_path('.'))
end
#
# Hook Script API bases
#
def srcdir_root
@ardir
end
def objdir_root
'.'
end
def relpath
'.'
end
#
# Option Parsing
#
def parsearg_global
while arg = ARGV.shift
case arg
when /\A\w+\z/
setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
return arg
when '-q', '--quiet'
@config.verbose = false
when '--verbose'
@config.verbose = true
when '--help'
print_usage $stdout
exit 0
when '--version'
puts "#{File.basename($0)} version #{Version}"
exit 0
when '--copyright'
puts Copyright
exit 0
else
setup_rb_error "unknown global option '#{arg}'"
end
end
nil
end
def valid_task?(t)
valid_task_re() =~ t
end
def valid_task_re
@valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
end
def parsearg_no_options
unless ARGV.empty?
task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
end
end
alias parsearg_show parsearg_no_options
alias parsearg_setup parsearg_no_options
alias parsearg_test parsearg_no_options
alias parsearg_clean parsearg_no_options
alias parsearg_distclean parsearg_no_options
def parsearg_config
evalopt = []
set = []
@config.config_opt = []
while i = ARGV.shift
if /\A--?\z/ =~ i
@config.config_opt = ARGV.dup
break
end
name, value = *@config.parse_opt(i)
if @config.value_config?(name)
@config[name] = value
else
evalopt.push [name, value]
end
set.push name
end
evalopt.each do |name, value|
@config.lookup(name).evaluate value, @config
end
# Check if configuration is valid
set.each do |n|
@config[n] if @config.value_config?(n)
end
end
def parsearg_install
@config.no_harm = false
@config.install_prefix = ''
while a = ARGV.shift
case a
when '--no-harm'
@config.no_harm = true
when /\A--prefix=/
path = a.split(/=/, 2)[1]
path = File.expand_path(path) unless path[0,1] == '/'
@config.install_prefix = path
else
setup_rb_error "install: unknown option #{a}"
end
end
end
def print_usage(out)
out.puts 'Typical Installation Procedure:'
out.puts " $ ruby #{File.basename $0} config"
out.puts " $ ruby #{File.basename $0} setup"
out.puts " # ruby #{File.basename $0} install (may require root privilege)"
out.puts
out.puts 'Detailed Usage:'
out.puts " ruby #{File.basename $0} "
out.puts " ruby #{File.basename $0} [] []"
fmt = " %-24s %s\n"
out.puts
out.puts 'Global options:'
out.printf fmt, '-q,--quiet', 'suppress message outputs'
out.printf fmt, ' --verbose', 'output messages verbosely'
out.printf fmt, ' --help', 'print this message'
out.printf fmt, ' --version', 'print version and quit'
out.printf fmt, ' --copyright', 'print copyright and quit'
out.puts
out.puts 'Tasks:'
TASKS.each do |name, desc|
out.printf fmt, name, desc
end
fmt = " %-24s %s [%s]\n"
out.puts
out.puts 'Options for CONFIG or ALL:'
@config.each do |item|
out.printf fmt, item.help_opt, item.description, item.help_default
end
out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
out.puts
out.puts 'Options for INSTALL:'
out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
out.printf fmt, '--prefix=path', 'install path prefix', ''
out.puts
end
#
# Task Handlers
#
def exec_config
@installer.exec_config
@config.save # must be final
end
def exec_setup
@installer.exec_setup
end
def exec_install
@installer.exec_install
end
def exec_test
@installer.exec_test
end
def exec_show
@config.each do |i|
printf "%-20s %s\n", i.name, i.value if i.value?
end
end
def exec_clean
@installer.exec_clean
end
def exec_distclean
@installer.exec_distclean
end
end # class ToplevelInstaller
class ToplevelInstallerMulti < ToplevelInstaller
include FileOperations
def initialize(ardir_root, config)
super
@packages = directories_of("#{@ardir}/packages")
raise 'no package exists' if @packages.empty?
@root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
end
def run_metaconfigs
@config.load_script "#{@ardir}/metaconfig", self
@packages.each do |name|
@config.load_script "#{@ardir}/packages/#{name}/metaconfig"
end
end
attr_reader :packages
def packages=(list)
raise 'package list is empty' if list.empty?
list.each do |name|
raise "directory packages/#{name} does not exist"\
unless File.dir?("#{@ardir}/packages/#{name}")
end
@packages = list
end
def init_installers
@installers = {}
@packages.each do |pack|
@installers[pack] = Installer.new(@config,
"#{@ardir}/packages/#{pack}",
"packages/#{pack}")
end
with = extract_selection(config('with'))
without = extract_selection(config('without'))
@selected = @installers.keys.select {|name|
(with.empty? or with.include?(name)) \
and not without.include?(name)
}
end
def extract_selection(list)
a = list.split(/,/)
a.each do |name|
setup_rb_error "no such package: #{name}" unless @installers.key?(name)
end
a
end
def print_usage(f)
super
f.puts 'Inluded packages:'
f.puts ' ' + @packages.sort.join(' ')
f.puts
end
#
# Task Handlers
#
def exec_config
run_hook 'pre-config'
each_selected_installers {|inst| inst.exec_config }
run_hook 'post-config'
@config.save # must be final
end
def exec_setup
run_hook 'pre-setup'
each_selected_installers {|inst| inst.exec_setup }
run_hook 'post-setup'
end
def exec_install
run_hook 'pre-install'
each_selected_installers {|inst| inst.exec_install }
run_hook 'post-install'
end
def exec_test
run_hook 'pre-test'
each_selected_installers {|inst| inst.exec_test }
run_hook 'post-test'
end
def exec_clean
rm_f @config.savefile
run_hook 'pre-clean'
each_selected_installers {|inst| inst.exec_clean }
run_hook 'post-clean'
end
def exec_distclean
rm_f @config.savefile
run_hook 'pre-distclean'
each_selected_installers {|inst| inst.exec_distclean }
run_hook 'post-distclean'
end
#
# lib
#
def each_selected_installers
Dir.mkdir 'packages' unless File.dir?('packages')
@selected.each do |pack|
$stderr.puts "Processing the package `#{pack}' ..." if verbose?
Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
Dir.chdir "packages/#{pack}"
yield @installers[pack]
Dir.chdir '../..'
end
end
def run_hook(id)
@root_installer.run_hook id
end
# module FileOperations requires this
def verbose?
@config.verbose?
end
# module FileOperations requires this
def no_harm?
@config.no_harm?
end
end # class ToplevelInstallerMulti
class Installer
FILETYPES = %w( bin lib ext data conf man )
include FileOperations
include HookScriptAPI
def initialize(config, srcroot, objroot)
@config = config
@srcdir = File.expand_path(srcroot)
@objdir = File.expand_path(objroot)
@currdir = '.'
end
def inspect
"#<#{self.class} #{File.basename(@srcdir)}>"
end
def noop(rel)
end
#
# Hook Script API base methods
#
def srcdir_root
@srcdir
end
def objdir_root
@objdir
end
def relpath
@currdir
end
#
# Config Access
#
# module FileOperations requires this
def verbose?
@config.verbose?
end
# module FileOperations requires this
def no_harm?
@config.no_harm?
end
def verbose_off
begin
save, @config.verbose = @config.verbose?, false
yield
ensure
@config.verbose = save
end
end
#
# TASK config
#
def exec_config
exec_task_traverse 'config'
end
alias config_dir_bin noop
alias config_dir_lib noop
def config_dir_ext(rel)
extconf if extdir?(curr_srcdir())
end
alias config_dir_data noop
alias config_dir_conf noop
alias config_dir_man noop
def extconf
ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
end
#
# TASK setup
#
def exec_setup
exec_task_traverse 'setup'
end
def setup_dir_bin(rel)
files_of(curr_srcdir()).each do |fname|
update_shebang_line "#{curr_srcdir()}/#{fname}"
end
end
alias setup_dir_lib noop
def setup_dir_ext(rel)
make if extdir?(curr_srcdir())
end
alias setup_dir_data noop
alias setup_dir_conf noop
alias setup_dir_man noop
def update_shebang_line(path)
return if no_harm?
return if config('shebang') == 'never'
old = Shebang.load(path)
if old
$stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1
new = new_shebang(old)
return if new.to_s == old.to_s
else
return unless config('shebang') == 'all'
new = Shebang.new(config('rubypath'))
end
$stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
open_atomic_writer(path) {|output|
File.open(path, 'rb') {|f|
f.gets if old # discard
output.puts new.to_s
output.print f.read
}
}
end
def new_shebang(old)
if /\Aruby/ =~ File.basename(old.cmd)
Shebang.new(config('rubypath'), old.args)
elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
Shebang.new(config('rubypath'), old.args[1..-1])
else
return old unless config('shebang') == 'all'
Shebang.new(config('rubypath'))
end
end
def open_atomic_writer(path, &block)
tmpfile = File.basename(path) + '.tmp'
begin
File.open(tmpfile, 'wb', &block)
File.rename tmpfile, File.basename(path)
ensure
File.unlink tmpfile if File.exist?(tmpfile)
end
end
class Shebang
def Shebang.load(path)
line = nil
File.open(path) {|f|
line = f.gets
}
return nil unless /\A#!/ =~ line
parse(line)
end
def Shebang.parse(line)
cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
new(cmd, args)
end
def initialize(cmd, args = [])
@cmd = cmd
@args = args
end
attr_reader :cmd
attr_reader :args
def to_s
"#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
end
end
#
# TASK install
#
def exec_install
rm_f 'InstalledFiles'
exec_task_traverse 'install'
end
def install_dir_bin(rel)
install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
end
def install_dir_lib(rel)
install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
end
def install_dir_ext(rel)
return unless extdir?(curr_srcdir())
install_files rubyextentions('.'),
"#{config('sodir')}/#{File.dirname(rel)}",
0555
end
def install_dir_data(rel)
install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
end
def install_dir_conf(rel)
# FIXME: should not remove current config files
# (rename preious file to .old/.org)
install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
end
def install_dir_man(rel)
install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
end
def install_files(list, dest, mode)
mkdir_p dest, @config.install_prefix
list.each do |fname|
install fname, dest, mode, @config.install_prefix
end
end
def libfiles
glob_reject(%w(*.y *.output), targetfiles())
end
def rubyextentions(dir)
ents = glob_select("*.#{@config.dllext}", targetfiles())
if ents.empty?
setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
end
ents
end
def targetfiles
mapdir(existfiles() - hookfiles())
end
def mapdir(ents)
ents.map {|ent|
if File.exist?(ent)
then ent # objdir
else "#{curr_srcdir()}/#{ent}" # srcdir
end
}
end
# picked up many entries from cvs-1.11.1/src/ignore.c
JUNK_FILES = %w(
core RCSLOG tags TAGS .make.state
.nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
*~ *.old *.bak *.BAK *.orig *.rej _$* *$
*.org *.in .*
)
def existfiles
glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
end
def hookfiles
%w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
%w( config setup install clean ).map {|t| sprintf(fmt, t) }
}.flatten
end
def glob_select(pat, ents)
re = globs2re([pat])
ents.select {|ent| re =~ ent }
end
def glob_reject(pats, ents)
re = globs2re(pats)
ents.reject {|ent| re =~ ent }
end
GLOB2REGEX = {
'.' => '\.',
'$' => '\$',
'#' => '\#',
'*' => '.*'
}
def globs2re(pats)
/\A(?:#{
pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
})\z/
end
#
# TASK test
#
TESTDIR = 'test'
def exec_test
unless File.directory?('test')
$stderr.puts 'no test in this package' if verbose?
return
end
$stderr.puts 'Running tests...' if verbose?
begin
require 'test/unit'
rescue LoadError
setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.'
end
runner = Test::Unit::AutoRunner.new(true)
runner.to_run << TESTDIR
runner.run
end
#
# TASK clean
#
def exec_clean
exec_task_traverse 'clean'
rm_f @config.savefile
rm_f 'InstalledFiles'
end
alias clean_dir_bin noop
alias clean_dir_lib noop
alias clean_dir_data noop
alias clean_dir_conf noop
alias clean_dir_man noop
def clean_dir_ext(rel)
return unless extdir?(curr_srcdir())
make 'clean' if File.file?('Makefile')
end
#
# TASK distclean
#
def exec_distclean
exec_task_traverse 'distclean'
rm_f @config.savefile
rm_f 'InstalledFiles'
end
alias distclean_dir_bin noop
alias distclean_dir_lib noop
def distclean_dir_ext(rel)
return unless extdir?(curr_srcdir())
make 'distclean' if File.file?('Makefile')
end
alias distclean_dir_data noop
alias distclean_dir_conf noop
alias distclean_dir_man noop
#
# Traversing
#
def exec_task_traverse(task)
run_hook "pre-#{task}"
FILETYPES.each do |type|
if type == 'ext' and config('without-ext') == 'yes'
$stderr.puts 'skipping ext/* by user option' if verbose?
next
end
traverse task, type, "#{task}_dir_#{type}"
end
run_hook "post-#{task}"
end
def traverse(task, rel, mid)
dive_into(rel) {
run_hook "pre-#{task}"
__send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
directories_of(curr_srcdir()).each do |d|
traverse task, "#{rel}/#{d}", mid
end
run_hook "post-#{task}"
}
end
def dive_into(rel)
return unless File.dir?("#{@srcdir}/#{rel}")
dir = File.basename(rel)
Dir.mkdir dir unless File.dir?(dir)
predir = Dir.pwd
Dir.chdir dir
$stderr.puts '---> ' + rel if verbose?
@currdir = rel
yield
Dir.chdir predir
$stderr.puts '<--- ' + rel if verbose?
@currdir = File.dirname(rel)
end
def run_hook(id)
path = [ "#{curr_srcdir()}/#{id}",
"#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
return unless path
begin
instance_eval File.read(path), path, 1
rescue
raise if $DEBUG
setup_rb_error "hook #{path} failed:\n" + $!.message
end
end
end # class Installer
class SetupError < StandardError; end
def setup_rb_error(msg)
raise SetupError, msg
end
if $0 == __FILE__
begin
ToplevelInstaller.invoke
rescue SetupError
raise if $DEBUG
$stderr.puts $!.message
$stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
exit 1
end
end
================================================
FILE: test/fixtures/bad_perms_test_key
================================================
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAywLYTlQ+keNiZI2FeBik6q34xStF3fF2+XPk3e21B0YQfbQI
xYJG8gbSro90Tu1hnEBZfYm+AC8HXsR9Kx9LpfTSa3aGFRREwdsi8xuaoeHWNxOh
MykU4UcxahT0Ft5+738kLmtVhw8bjjkqcpxCSgrpcJbad2B2ft1KBE02kiU2y7yS
92sUSWBzVkkJTmiWBDvn5pT9y6IpVCKseWbumGQ6nozEfXe4ihUsKAH42XPxSXMX
Xe64JuONQlxZPrqyF7L/lt6ZeyKQ7yXVcxr6P4W3rPqlBhq5yXNI3vV67KUNCh+w
HH6RLagaguS0VTdBYwo3dvkmuL6TJqnzPzrv1wIBIwKCAQBczhm9aFceHs3k0vsv
ljzDDbONVZxIM96d69ZW0xDtYdunLbrd8mmTNlGu4n5P930UOqyRKQZme+YcsZhO
OjE17ELvTCAJot2acs4l/f2L1PQ2b1+iW+xJuiU3zxB/e6e98PqY4Jwge+9W9Y+/
XWAh2RpCGRNpxbKDI1UJR95usRxhxlmY0RmUtpDjc3qEOPRgj7E2iC4fFVdvYzKt
FBkGR6Gv2/jqm+c/TmRCFnyaWJXC8W6LkkACjUc6IKUOBSbs6l+3DH0PBOr+XbGv
peExAfYCTfvGy/NNPpzP0JJUJ92sWaZUPUTf8McSsgrTTRBfklsJuf9Hje/8abcr
AheLAoGBAPGX3CDMU68BTgO8bOvGhFHQSu9AypIhEh0PrE1CYsEvYrIRg9h23fuz
1nz7Z6kIYTNMywxfphEL65CTiJ5eSJV+UGMFAKYziCwH7MDD4Wc6cPLrGmA0eYDz
QMUQJwqfuXKq/dMw2VB6qGbNmbERWmq3wteF/afyTatd6poV4UcXAoGBANcd/o4s
zf1SwxKoT4GGp62gtqT+gIbGkV0AzWBcaDarRNbcV7uFZLL7wzPcCR8/cPZiN+ks
+LsVYvDfrFHRxtdzIesxcqG2v6LazB/+/rZp4Hqc1a4OwxSdOYzINjA01GV2HcET
XzoJIQA8ZYuVpELaNzjLtKBZ6mrQmBh7RRVBAoGBAJ7C4RzeCxs7XycGyzvaObIb
Ke2uO/mgtCG516B8FQKbe18S0vv2V1xC+qnnCZr24MnworBcHKwdxq925L/Xjsij
dqd4UOI/Hvhc+qqPWZubbsuEjazvSIfwTyJps0F+55R+/pIYyVIkt86HG9rCQrso
TNbFw/IFoMER1K5mJlNJAoGAT+aRv8d/tdzpXrOLPrz8c7C43jKkxFhh4LcnthOx
refXvYUKpLyEfP5tE0MZVL/K3yvLn8A/IOqvuI2Xxp5fzF32p9CJqceJAfl/BJHp
lDX0SsyJ4ZB0WB0kARcqEee8mrbX2f/hipWtK/ktB/XAqx3Z/ycXND6nhsKBorFx
bksCgYEAtPns7oc98wmF8j37ELikMFJOvQgccbGTzcJ1RVzji95HfN4p3kA6emo/
hRyP8XNedEI/CD6MmthKKymw5Ck1I51nwDW4+zTEDAN14nh4T9p6Jqe7lSENqLaY
A5jkgXV8e0YqKkjLNec5hAzoEY09KryQdz81KXeHvbEUfIiItTk=
-----END RSA PRIVATE KEY-----
================================================
FILE: test/fixtures/chef/recipes/sudo/attributes/sudoers.rb
================================================
#
# Cookbook Name:: sudo
# Attribute File:: sudoers
#
# Copyright 2008, 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.
#
authorization Mash.new unless attribute?("authorization")
authorization[:sudo] = Mash.new unless authorization.has_key?(:sudo)
unless authorization[:sudo].has_key?(:groups)
authorization[:sudo][:groups] = Array.new
end
unless authorization[:sudo].has_key?(:users)
authorization[:sudo][:users] = Array.new
end
================================================
FILE: test/fixtures/chef/recipes/sudo/recipes/default.rb
================================================
#
# Cookbook Name:: sudo
# Recipe:: default
#
# Copyright 2008, 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
================================================
FILE: test/fixtures/chef/recipes/sudo/templates/default/sudoers.erb
================================================
#
# /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 -%>
================================================
FILE: test/fixtures/clouds/rds_cloud.rb
================================================
# test RDS pool
pool :poolparty do
cloud :fake_cloud do
keypair File.dirname(__FILE__)+"/../keys/test_key"
using :ec2
rds :db1 do
username "admin"
password "secret"
storage 5
end
end
end
================================================
FILE: test/fixtures/clouds/rds_missing_params.rb
================================================
# test RDS pool
pool :poolparty do
cloud :fake_cloud do
keypair File.dirname(__FILE__)+"/../keys/test_key"
using :ec2
rds :db1 do
end
end
end
================================================
FILE: test/fixtures/clouds/simple_cloud.rb
================================================
# Poolparty spec
pool "poolparty" do
cloud :fake_cloud do
keypair File.dirname(__FILE__)+"/../keys/test_key"
using :ec2 do
end
end
end
================================================
FILE: test/fixtures/clouds/ssh_cloud.rb
================================================
pool :tssh do
cloud :tgarden do
instances 2
keypair File.join(FIXTURES_PATH, 'keys/test_key')
using :ssh do
user 'fairchild' #default is root
hosts %w(beet squash)
end
has_file '/etc/poolparty/welcome', :content=>"Welcome to the #{self.name} cloud"
end
end
================================================
FILE: test/fixtures/clouds/vmware_cloud.rb
================================================
pool "local" do
cloud "local_app" do
keypair "id_rsa"
using :vmware do
image_id "/Users/alerner/Documents/vm/Ubuntu32bitVM.vmwarevm/Ubuntu32bitVM.vmx"
public_ip "192.168.248.133"
end
has_file "/etc/motd", :content => "BURN! Pocket Aces"
apache
end
end
================================================
FILE: test/fixtures/ec2/ec2-describe-instances_response_body.xml
================================================
89d95a27-25b1-47bd-82a2-a70a936e0913r-c72545ae481524450359poolparty-fake_cloudi-7fd89416ami-bf5eb9d616runningip-10-250-46-144.ec2.internalec2-75-101-141-103.compute-1.amazonaws.comtest_key0m1.small2009-05-31T17:56:42.000Zus-east-1caki-a71cf9ceari-a51cf9ccr-e9b4ff80481524450359poolparty-fake_cloudi-7f000516ami-0d72946416runningip-10-251-74-198.ec2.internalec2-67-202-10-73.compute-1.amazonaws.comtest_key0m1.small2009-07-20T20:35:51.000Zus-east-1caki-a71cf9ceari-a51cf9cc
================================================
FILE: test/fixtures/ec2/ec2-describe-security-groups_response_body.xml
================================================
1234567890poolparty-fake_cloudpoolparty-fake_cloudtcp80800.0.0.0/0
================================================
FILE: test/fixtures/ec2/ec2-run-instances_response_body.xml
================================================
c1128a8d-59e6-4aad-8923-12b07ff873ecr-51D70925adminadmin-defaulti-485C07DEemi-39CA160F0pending0.0.0.00.0.0.0eucalyptus_sample0m1.small2009-07-22T00:09:29.293Zgardeneki-AE9D17DDeri-171A1927false
================================================
FILE: test/fixtures/ec2/ec2-terminate-instances_response_body.xml
================================================
d16d104e-f450-4d5c-8354-70a8cad2dacdi-3B3506A032shutting-down16running
================================================
FILE: test/fixtures/ec2/elb-describe-load-balancers.xml
================================================
hemant-watson-lbhemant-watson-lb-1501915372.us-east-1.elb.amazonaws.comHTTP808080us-east-1ci-61835409i-ab8453c3TCP:80803052102009-09-22T22:00:35.790Zminerva-poolminerva-pool-1740816084.us-east-1.elb.amazonaws.comHTTP8080us-east-1ai-49b67f21i-a3935dcbi-ad26efc5i-f531f89dTCP:803052102009-09-18T01:42:43.470Z61cc2880-a890-11de-8090-bb69e1dec4b2
================================================
FILE: test/fixtures/ec2/rds-describe-db-instances-empty_response_body.xml
================================================
5e547870-fa2d-11de-8e05-53428aa3290f
================================================
FILE: test/fixtures/keys/pem_key.pem
================================================
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAywLYTlQ+keNiZI2FeBik6q34xStF3fF2+XPk3e21B0YQfbQI
xYJG8gbSro90Tu1hnEBZfYm+AC8HXsR9Kx9LpfTSa3aGFRREwdsi8xuaoeHWNxOh
MykU4UcxahT0Ft5+738kLmtVhw8bjjkqcpxCSgrpcJbad2B2ft1KBE02kiU2y7yS
92sUSWBzVkkJTmiWBDvn5pT9y6IpVCKseWbumGQ6nozEfXe4ihUsKAH42XPxSXMX
Xe64JuONQlxZPrqyF7L/lt6ZeyKQ7yXVcxr6P4W3rPqlBhq5yXNI3vV67KUNCh+w
HH6RLagaguS0VTdBYwo3dvkmuL6TJqnzPzrv1wIBIwKCAQBczhm9aFceHs3k0vsv
ljzDDbONVZxIM96d69ZW0xDtYdunLbrd8mmTNlGu4n5P930UOqyRKQZme+YcsZhO
OjE17ELvTCAJot2acs4l/f2L1PQ2b1+iW+xJuiU3zxB/e6e98PqY4Jwge+9W9Y+/
XWAh2RpCGRNpxbKDI1UJR95usRxhxlmY0RmUtpDjc3qEOPRgj7E2iC4fFVdvYzKt
FBkGR6Gv2/jqm+c/TmRCFnyaWJXC8W6LkkACjUc6IKUOBSbs6l+3DH0PBOr+XbGv
peExAfYCTfvGy/NNPpzP0JJUJ92sWaZUPUTf8McSsgrTTRBfklsJuf9Hje/8abcr
AheLAoGBAPGX3CDMU68BTgO8bOvGhFHQSu9AypIhEh0PrE1CYsEvYrIRg9h23fuz
1nz7Z6kIYTNMywxfphEL65CTiJ5eSJV+UGMFAKYziCwH7MDD4Wc6cPLrGmA0eYDz
QMUQJwqfuXKq/dMw2VB6qGbNmbERWmq3wteF/afyTatd6poV4UcXAoGBANcd/o4s
zf1SwxKoT4GGp62gtqT+gIbGkV0AzWBcaDarRNbcV7uFZLL7wzPcCR8/cPZiN+ks
+LsVYvDfrFHRxtdzIesxcqG2v6LazB/+/rZp4Hqc1a4OwxSdOYzINjA01GV2HcET
XzoJIQA8ZYuVpELaNzjLtKBZ6mrQmBh7RRVBAoGBAJ7C4RzeCxs7XycGyzvaObIb
Ke2uO/mgtCG516B8FQKbe18S0vv2V1xC+qnnCZr24MnworBcHKwdxq925L/Xjsij
dqd4UOI/Hvhc+qqPWZubbsuEjazvSIfwTyJps0F+55R+/pIYyVIkt86HG9rCQrso
TNbFw/IFoMER1K5mJlNJAoGAT+aRv8d/tdzpXrOLPrz8c7C43jKkxFhh4LcnthOx
refXvYUKpLyEfP5tE0MZVL/K3yvLn8A/IOqvuI2Xxp5fzF32p9CJqceJAfl/BJHp
lDX0SsyJ4ZB0WB0kARcqEee8mrbX2f/hipWtK/ktB/XAqx3Z/ycXND6nhsKBorFx
bksCgYEAtPns7oc98wmF8j37ELikMFJOvQgccbGTzcJ1RVzji95HfN4p3kA6emo/
hRyP8XNedEI/CD6MmthKKymw5Ck1I51nwDW4+zTEDAN14nh4T9p6Jqe7lSENqLaY
A5jkgXV8e0YqKkjLNec5hAzoEY09KryQdz81KXeHvbEUfIiItTk=
-----END RSA PRIVATE KEY-----
================================================
FILE: test/fixtures/keys/pem_pub_key.pem
================================================
-----BEGIN RSA PUBLIC KEY-----
MIIBCAKCAQEAywLYTlQ+keNiZI2FeBik6q34xStF3fF2+XPk3e21B0YQfbQIxYJG
8gbSro90Tu1hnEBZfYm+AC8HXsR9Kx9LpfTSa3aGFRREwdsi8xuaoeHWNxOhMykU
4UcxahT0Ft5+738kLmtVhw8bjjkqcpxCSgrpcJbad2B2ft1KBE02kiU2y7yS92sU
SWBzVkkJTmiWBDvn5pT9y6IpVCKseWbumGQ6nozEfXe4ihUsKAH42XPxSXMXXe64
JuONQlxZPrqyF7L/lt6ZeyKQ7yXVcxr6P4W3rPqlBhq5yXNI3vV67KUNCh+wHH6R
LagaguS0VTdBYwo3dvkmuL6TJqnzPzrv1wIBIw==
-----END RSA PUBLIC KEY-----
================================================
FILE: test/fixtures/keys/test_key
================================================
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAywLYTlQ+keNiZI2FeBik6q34xStF3fF2+XPk3e21B0YQfbQI
xYJG8gbSro90Tu1hnEBZfYm+AC8HXsR9Kx9LpfTSa3aGFRREwdsi8xuaoeHWNxOh
MykU4UcxahT0Ft5+738kLmtVhw8bjjkqcpxCSgrpcJbad2B2ft1KBE02kiU2y7yS
92sUSWBzVkkJTmiWBDvn5pT9y6IpVCKseWbumGQ6nozEfXe4ihUsKAH42XPxSXMX
Xe64JuONQlxZPrqyF7L/lt6ZeyKQ7yXVcxr6P4W3rPqlBhq5yXNI3vV67KUNCh+w
HH6RLagaguS0VTdBYwo3dvkmuL6TJqnzPzrv1wIBIwKCAQBczhm9aFceHs3k0vsv
ljzDDbONVZxIM96d69ZW0xDtYdunLbrd8mmTNlGu4n5P930UOqyRKQZme+YcsZhO
OjE17ELvTCAJot2acs4l/f2L1PQ2b1+iW+xJuiU3zxB/e6e98PqY4Jwge+9W9Y+/
XWAh2RpCGRNpxbKDI1UJR95usRxhxlmY0RmUtpDjc3qEOPRgj7E2iC4fFVdvYzKt
FBkGR6Gv2/jqm+c/TmRCFnyaWJXC8W6LkkACjUc6IKUOBSbs6l+3DH0PBOr+XbGv
peExAfYCTfvGy/NNPpzP0JJUJ92sWaZUPUTf8McSsgrTTRBfklsJuf9Hje/8abcr
AheLAoGBAPGX3CDMU68BTgO8bOvGhFHQSu9AypIhEh0PrE1CYsEvYrIRg9h23fuz
1nz7Z6kIYTNMywxfphEL65CTiJ5eSJV+UGMFAKYziCwH7MDD4Wc6cPLrGmA0eYDz
QMUQJwqfuXKq/dMw2VB6qGbNmbERWmq3wteF/afyTatd6poV4UcXAoGBANcd/o4s
zf1SwxKoT4GGp62gtqT+gIbGkV0AzWBcaDarRNbcV7uFZLL7wzPcCR8/cPZiN+ks
+LsVYvDfrFHRxtdzIesxcqG2v6LazB/+/rZp4Hqc1a4OwxSdOYzINjA01GV2HcET
XzoJIQA8ZYuVpELaNzjLtKBZ6mrQmBh7RRVBAoGBAJ7C4RzeCxs7XycGyzvaObIb
Ke2uO/mgtCG516B8FQKbe18S0vv2V1xC+qnnCZr24MnworBcHKwdxq925L/Xjsij
dqd4UOI/Hvhc+qqPWZubbsuEjazvSIfwTyJps0F+55R+/pIYyVIkt86HG9rCQrso
TNbFw/IFoMER1K5mJlNJAoGAT+aRv8d/tdzpXrOLPrz8c7C43jKkxFhh4LcnthOx
refXvYUKpLyEfP5tE0MZVL/K3yvLn8A/IOqvuI2Xxp5fzF32p9CJqceJAfl/BJHp
lDX0SsyJ4ZB0WB0kARcqEee8mrbX2f/hipWtK/ktB/XAqx3Z/ycXND6nhsKBorFx
bksCgYEAtPns7oc98wmF8j37ELikMFJOvQgccbGTzcJ1RVzji95HfN4p3kA6emo/
hRyP8XNedEI/CD6MmthKKymw5Ck1I51nwDW4+zTEDAN14nh4T9p6Jqe7lSENqLaY
A5jkgXV8e0YqKkjLNec5hAzoEY09KryQdz81KXeHvbEUfIiItTk=
-----END RSA PRIVATE KEY-----
================================================
FILE: test/fixtures/keys/test_pub_key
================================================
-----BEGIN RSA PUBLIC KEY-----
MIIBCAKCAQEAywLYTlQ+keNiZI2FeBik6q34xStF3fF2+XPk3e21B0YQfbQIxYJG
8gbSro90Tu1hnEBZfYm+AC8HXsR9Kx9LpfTSa3aGFRREwdsi8xuaoeHWNxOhMykU
4UcxahT0Ft5+738kLmtVhw8bjjkqcpxCSgrpcJbad2B2ft1KBE02kiU2y7yS92sU
SWBzVkkJTmiWBDvn5pT9y6IpVCKseWbumGQ6nozEfXe4ihUsKAH42XPxSXMXXe64
JuONQlxZPrqyF7L/lt6ZeyKQ7yXVcxr6P4W3rPqlBhq5yXNI3vV67KUNCh+wHH6R
LagaguS0VTdBYwo3dvkmuL6TJqnzPzrv1wIBIw==
-----END RSA PUBLIC KEY-----
================================================
FILE: test/fixtures/resources/fake_plugin.rb
================================================
module PoolParty
module Resources
class FakePlugin < Resource
def self.has_method_name
"fake_plugin"
end
def name
"fake_plugin"
end
def after_loaded
has_file "/etc/my_configs/special_config" do
requires get_directory("/etc/my_configs")
end
has_directory("/etc/my_configs")
end
end
end
end
================================================
FILE: test/fixtures/resources/fake_resource.rb
================================================
class FakeResource < PoolParty::Resource
default_options(
:name => nil
)
def self.has_method_name
"tester"
end
def print_to_chef
<<-EOE
fake "<%= name %>" do
content "<%= content %>"
end
EOE
end
end
================================================
FILE: test/fixtures/resources/fake_subclassed_plugin.rb
================================================
module PoolParty
module Resources
class FakeSubclassedPlugin < Resource
def self.has_method_name
"subclassed"
end
def after_loaded
has_file "/etc/my_configs/special_config" do
requires get_directory("/etc/my_configs")
end
end
end
end
end
================================================
FILE: test/fixtures/resources/random_proc_file.rb
================================================
a = []
@b = Proc.new do
@str = "I have a bee"
end
@c = "cats"
================================================
FILE: test/fixtures/templates/apache_conf.erb
================================================
# Apache conf file
apache <%= cloud.name %>
================================================
FILE: test/fixtures/test_template.erb
================================================
Hello <%= friends %>
================================================
FILE: test/lib/core/array_test.rb
================================================
require "test_helper"
class ArrayTest < Test::Unit::TestCase
def setup
@arr = %w(a b c d)
@hash_arr = [
{:name => "peter", :occupation => "computer scientist"},
{:name => "al", :occupation => "computer scientist"},
{:name => "matt", :occupation => "doctor"},
{:name => "jenna", :occupation => "lawyer"}
]
end
def test_be_able_to_select_with_hash
assert_equal @hash_arr.select_with_hash(:name => "matt").first[:occupation], "doctor"
assert_equal @hash_arr.select_with_hash(:occupation => "computer scientist").first[:name], "peter"
assert @hash_arr.select_with_hash(:occupation => "matt").empty?
end
end
================================================
FILE: test/lib/core/hash_test.rb
================================================
require 'test_helper'
class HashTest < Test::Unit::TestCase
context "Hash" do
setup do
@hsh = {:a => "a", :b => "b", :c => "c"}
end
should "have choose to select hashes" do
assert_equal @hsh.choose {|k,v| k == :a}, {:a => "a"}
assert_equal @hsh.choose {|k,v| k == :z}, {}
assert_equal @hsh.choose {|k,v|[:b,:a].include?(k)}.keys.sort{|a,b|"#{a}"<=>"#{b}"}, [:a,:b]
end
should "be able to call methods on the hash of their keys" do
assert_equal @hsh.a, "a"
assert_equal @hsh.b, "b"
assert_equal @hsh.c, "c"
end
should "compute the differences in the hashes" do
assert_equal ({:a => "a"}).diff({:a => "a"}), {}
assert_equal ({:a => "a"}).diff({:a => "b"}), {:a => "b"}
assert_equal ({:a => "a", :b => "b"}).diff({:a => "b"}, :a), {:a => "b"}
assert_equal ({:a => "a", :b => "b"}).diff({:b => "b"}, :b), {}
assert_equal ({:a => "a", :b => "b"}).diff({:b => "c"}, :b), {:b => "c"}
assert_equal ({:a => "a", :b => "b"}).diff({:b => "c"}), {:b => "c", :a => nil}
assert_equal ({:a => "a", :b => "b"}).diff({:b => "c"}, :a), {:a => nil}
end
end
end
================================================
FILE: test/lib/core/object_test.rb
================================================
require 'test_helper'
class ObjectTest < Test::Unit::TestCase
context "object" do
setup do
reset!
end
context "global methods" do
setup do
@o = Object.new
end
should "have the pools method" do
assert @o.respond_to?(:pool)
end
should "make a pool when calling pool" do
assert_nil @@pool
@@pool = pool :fun_pool do
end
assert_not_nil @@pool
end
end
end
end
================================================
FILE: test/lib/core/string_test.rb
================================================
require 'test_helper'
class StringTest < Test::Unit::TestCase
context "string" do
should "camelcase properly" do
assert_equal "DocRiver", "doc_river".camelcase
assert_equal "AWholeLottaHotdogs", "a_whole_lotta_hotdogs".camelcase
assert_equal "One", "one".camelcase
end
should "snake_case properly" do
assert_equal "one", "one".snake_case
assert_equal "plenty_of_moons", "PlentyOfMoons".snake_case
assert_equal "girls_make_boys_make_girls", "GirlsMakeBoysMakeGirls".snake_case
end
should "dasherize properly" do
assert_equal "mini-coopers-rock", "MiniCoopersRock".dasherize
assert_equal "only-the-best", "OnlyTheBest".dasherize
assert_equal "one", "one".dasherize
end
should "classify properly" do
assert_equal "ABird", "a_bird".classify
assert_equal "Macguyver", "macguyver".classify
assert_equal "RiceAndBeans", "rice_and_beans".classify
assert_equal "Rice::And::Beans", "rice::and::beans".classify
assert_equal "Pepper", "dr.pepper".classify
assert_equal "Dr::Pepper", "dr::pepper".classify
end
should "have the / for filepaths" do
assert_equal "/root/home/stuff", "/root"/"home"/"stuff"
assert_equal "/root/box", "/root" / "box"
end
end
end
================================================
FILE: test/lib/core/symbol_test.rb
================================================
require 'test_helper'
class SymbolTest < Test::Unit::TestCase
context "Symbol" do
should "be able to compare to strings" do
assert_equal [:a, :c, :b].sort, [:a, :b, :c]
assert_nothing_raised do
:a <=> :b
end
end
should "have path separaters" do
assert_equal :a / :b, "a/b"
end
should "classify the symbol" do
assert_equal :C, :c.classify
end
end
end
================================================
FILE: test/lib/poolparty/cloud_test.rb
================================================
require 'test_helper'
# require 'rr'
class CloudTest < Test::Unit::TestCase
# include RR::Adapters::TestUnit
def setup
clear!
@filepath = File.join(FIXTURES_PATH, "clouds/simple_cloud.rb")
require @filepath
@cloud = pool.clouds[pool.clouds.keys.first]
stub_ec2_calls
end
def test_have_a_pool_name
assert_equal pool.name, @cloud.pool.name
end
def test_have_a_keypair
assert_not_nil @cloud.keypair
assert_equal 'test_key', @cloud.keypair.basename
end
def test_be_using_ec2_cloud_provider_by_default
assert_equal :ec2, @cloud.cloud_provider.name
assert_kind_of ::CloudProviders::Ec2, @cloud.cloud_provider
end
def test_set_the_cloud_provider_cloud_and_keypair_with_cloud_provider
assert_equal @cloud, @cloud.cloud_provider.cloud
assert_equal @cloud.keypair.basename, @cloud.cloud_provider.keypair.basename
end
def test_set_the_cloud_provider_with_a_using_block
@cloud.instance_eval do
using :ec2
keypair "test_key", File.join(FIXTURES_PATH, "keys")
image_id 'emi-39921602'
end
assert_equal :ec2, @cloud.cloud_provider.name
assert_equal CloudProviders::Ec2, @cloud.cloud_provider.class
assert_equal "emi-39921602", @cloud.cloud_provider.image_id
end
def test_nodes
assert_respond_to @cloud, :nodes
assert_respond_to @cloud.nodes, :each
assert @cloud.nodes.size>1, "Cloudsize was: #{@cloud.nodes.size}"
end
def test_run
# WHAT?
# result = @cloud.run('uptime')
# assert_match /uptime/, result["app"]
end
def test_expansion
#TODO: improve this test
# size = @cloud.nodes.size
# assert_equal size+1, @cloud.expand.nodes.size
# assert_nothing_raised @cloud.expand
end
def test_contract!
#TODO: need to better mock the terminate! ec2 call
# size = @cloud.nodes.size
# result = @cloud.contract!
# assert_equal 'shuttin-down', result.status
# assert_equal size-1, @cloud.nodes.size
end
def test_change_ssh_port
clear!
pool "ssh_port" do
cloud "babity" do
keypair "test_key"
ssh_port 1922
end
end
assert_equal 1922, clouds["babity"].ssh_port
assert_equal 22, clouds["noneity"].ssh_port
end
def test_change_ssh_port
clear!
pool "ssher" do
cloud "custom" do
keypair "test_key"
# ssh_options("-P" => "1992")
end
cloud "noneity" do
keypair "test_key"
end
end
# assert_equal "1992", clouds["custom"].ssh_options["-P"]
end
end
================================================
FILE: test/lib/poolparty/keypair_test.rb
================================================
require 'test_helper'
class KeypairTest < Test::Unit::TestCase
context "Base" do
setup do
Keypair.searchable_paths << File.join(FIXTURES_PATH, "keys")
@keypair = Keypair.new(File.join(FIXTURES_PATH, "keys", "test_key"))
@keypair_pem = Keypair.new(File.join(FIXTURES_PATH, "keys", "pem_key"))
end
should "set the file given as the file for the keypair" do
assert_equal @keypair.filepath, File.join(FIXTURES_PATH, "keys", "test_key")
assert_equal @keypair.full_filepath, File.expand_path(File.join(FIXTURES_PATH, "keys", "test_key"))
assert_match @keypair.to_s, File.expand_path(File.join(FIXTURES_PATH, "keys", "test_key"))
end
should "find the suffixed file given without pem suffix" do
assert_equal @keypair_pem.filepath, File.join(FIXTURES_PATH, "keys", "pem_key")
assert_equal @keypair_pem.full_filepath, File.expand_path(File.join(FIXTURES_PATH, "keys", "pem_key.pem"))
assert_match @keypair_pem.to_s, File.expand_path(File.join(FIXTURES_PATH, "keys", "pem_key"))
end
should "find the suffixed file given without pem suffix or dir" do
basename = File.basename(@keypair_pem.full_filepath)
basename_no_suffix = File.basename(basename, '.pem')
search_dirs = [File.dirname(@keypair_pem.full_filepath)]
keypair = Keypair.new(basename, search_dirs)
keypair_no_suffix = Keypair.new(basename_no_suffix, search_dirs)
assert_equal keypair.full_filepath, @keypair_pem.full_filepath
assert_equal keypair_no_suffix.full_filepath, @keypair_pem.full_filepath
end
should "have the content of the file available" do
assert_equal @keypair.content, open(File.join(FIXTURES_PATH, "keys", "test_key")).read
end
should "be able to generate the public key from the private" # do
# # assert_equal @keypair.public_key, "#{open(FIXTURES_PATH/"test_pub_key").read}"
# end
should "have the basename of the keypair" do
assert_equal @keypair.basename, "test_key"
assert_equal @keypair.filename, "test_key"
end
should "be valid if it's 600 permissions" do
assert @keypair.valid?
end
should "be invalid if the file permissions are executable" do
assert_raises StandardError do
Keypair.new(File.join(FIXTURES_PATH, "bad_perms_test_key")).valid?
end
end
end
end
================================================
FILE: test/lib/poolparty/pool_party_error_test.rb
================================================
require 'test_helper'
class PoolPartyErrorTest < Test::Unit::TestCase
include PoolParty
context "Error" do
should "be able to create a PoolPartyError" do
assert_nothing_raised do
PoolPartyError.create "TestError", "New error"
end
end
should "raise when called with the test error" do
assert_raise TestError do
raise PoolPartyError.create("TestError", "New error")
end
end
end
end
================================================
FILE: test/lib/poolparty/pool_test.rb
================================================
require 'test_helper'
class PoolTest < Test::Unit::TestCase
def setup
stub_keypair_searchable_paths
end
def test_set_up_pool_object
reset!
pool "hi" do
end
assert_equal @@pool.name, "hi"
end
end
================================================
FILE: test/lib/poolparty/rds_test.rb
================================================
require 'test_helper'
class RdsTest < Test::Unit::TestCase
def setup
stub_ec2_calls
stub_response(AWS::EC2::Base, :describe_security_groups, 'ec2-describe-security-groups')
stub_response(AWS::EC2::Base, :run_instances, 'ec2-run-instances')
stub_response(AWS::RDS::Base, :describe_db_instances, 'rds-describe-db-instances-empty')
reset!
end
def test_basic
scenario "rds_cloud"
end
def test_required_properties
assert_raises(RuntimeError) { scenario "rds_missing_params" }
end
private
def scenario(filename)
clear!
@filepath = File.join(FIXTURES_PATH, "clouds/#{filename}.rb")
require @filepath
@cloud = pool.clouds[pool.clouds.keys.first]
@cloud.run
end
def stub_response(klass, method, fixture_filename)
klass.any_instance.stubs(method).returns AWS::Response.parse(:xml => open(File.join(FIXTURES_PATH, "ec2/#{fixture_filename}_response_body.xml")).read)
end
end
================================================
FILE: test/test_helper.rb
================================================
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
$LOAD_PATH.unshift(File.dirname(__FILE__))
ENV['RACK_ENV'] ||= 'test'
# Test dependencies
%w(fakeweb right_http_connection matchy shoulda).each do |dep|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__),'..', 'vendor/gems', dep, 'lib'))
# require "#{dep}"
end
require 'poolparty'
require "rubygems"
require "test/unit"
# TODO: Rip out shoulda
require "shoulda"
require 'mocha' # ARG! don't want to introduce additional test dependencies, but FakeWeb can only handle one fake post per URL!!!
require 'webmock/test_unit'
require 'pathname'
require 'git-style-binary/command'
require "test_methods"
GitStyleBinary.run = true
FIXTURES_PATH = Pathname.new(File.expand_path('../fixtures', __FILE__))
================================================
FILE: test/test_methods.rb
================================================
class Test::Unit::TestCase
# Helpers
def FIXTURES_PATH
"#{::File.dirname(__FILE__)}/fixtures"
end
def test_dir
"#{File.dirname(__FILE__)}/test_dir"
end
def clear!
$pools = $clouds = nil
end
def modify_env_with_hash(h={})
orig_env = Kernel.const_get(:ENV)
h.each do |k,v|
orig_env.delete(k)
orig_env[k] = v
orig_env[k].freeze
end
if RUBY_VERSION.scan(/1.8/).pop
# Kernel.send :remove_const, 'ENV' if Kernel.const_defined?('ENV')
Kernel.stubs(:ENV).returns(orig_env)
elsif RUBY_VERSION.scan(/1.9/).pop
Object.stubs(:ENV).returns(orig_env)
# Object.send :remove_const, 'ENV' if Object.const_defined?('ENV')
else
raise "can't determine what version of ruby you are running."
end
# Kernel.const_set(:ENV, orig_env)
end
def capture_stdout(&block)
old_stdout = $stdout
old_stderr = $stderr
out = StringIO.new
$stdout = out
old_stderr = StringIO.new
begin
block.call if block
ensure
$stdout = old_stdout
$stderr = old_stderr
end
out.string
end
def stub_keypair_searchable_paths
Keypair.searchable_paths << File.join(FIXTURES_PATH, "keys")
end
def read_fixture(path)
open(File.join(FIXTURES_PATH, "ec2", path)).read
end
def stub_ec2_calls(&block)
stub_keypair_searchable_paths
modify_env_with_hash(
"EC2_ACCESS_KEY" => "fake_access_key",
"EC2_SECRET_KEY" => "fake_secret_key",
"EC2_PRIVATE_KEY" => ::File.dirname(__FILE__) + "/fixtures/keys/test_key",
"EC2_CERT" => ::File.dirname(__FILE__) + "/fixtures/keys/test_key",
"EC2_USER_ID" => '1234567890'
)
# FakeWeb.allow_net_connect=false
stub_request(:get, /.*Action=DescribeInstances.*/).
to_return(:status => 200, :body => read_fixture('ec2-describe-instances_response_body.xml'))
stub_request(:get, /.*Action=RunInstances.*/).
to_return(:status => 200, :body => read_fixture('ec2-run-instances_response_body.xml'))
stub_request(:get, /.*Action=TerminateInstances.*/).
to_return(:status => 200, :body => read_fixture('ec2-terminate-instances_response_body.xml'))
stub_request(:post, /elasticloadbalancing\.amazonaws\.com/).
to_return(:status => 200, :body => read_fixture('elb-describe-load-balancers.xml'))
stub_request(:post, /\//).
to_return(:status => 200, :body => read_fixture('ec2-describe-instances_response_body.xml'))
# FakeWeb.register_uri(:get, /.*Action=TerminateInstances.*/, :status => ["200", "OK"],
# :body => open(FIXTURES_PATH/"ec2/ec2-terminate-instances_response_body.xml").read)
#
# FakeWeb.register_uri(:post, /elasticloadbalancing\.amazonaws\.com/, :status => ["200", "OK"],
# :body => open(FIXTURES_PATH/"ec2/elb-describe-load-balancers.xml").read)
#
# FakeWeb.register_uri(:post, /\//, :status => ["200", "OK"],
# :body => open(FIXTURES_PATH/"ec2/ec2-describe-instances_response_body.xml").read)
instance_eval &block if block
end
end
================================================
FILE: vendor/gems/dslify/.gitignore
================================================
pkg/*
pkg/
tmp
================================================
FILE: vendor/gems/dslify/LICENSE
================================================
Copyright (c) 2009 Ari Lerner
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.
================================================
FILE: vendor/gems/dslify/README.rdoc
================================================
= dslify
Dslify, born out of a need for improvement on Dslify
Add dsl accessors to any class.
Usage:
class MyClass
include Dslify
dsl_methods :award, :people
end
mc = MyClass.new
mc.award "Tony Award"
mc.people ["Bob", "Frank", "Ben"]
You can set defaults as well:
class MyClass
default_options :award => "Tony Award"
end
Finally, if your tree of available accessors runs higher and longer than
just 1 file, for instance, if you use Parenting, you can set forwarders to
forward the query up the chain
class MyClass
forwards_to :parent
end
== Copyright
Copyright (c) 2009 Ari Lerner. See LICENSE for details.
================================================
FILE: vendor/gems/dslify/Rakefile
================================================
require 'rubygems'
require 'rake'
begin
require 'jeweler'
Jeweler::Tasks.new do |gem|
gem.name = "dslify"
gem.summary = %Q{TODO}
gem.email = "arilerner@mac.com"
gem.homepage = "http://github.com/auser/dslify"
gem.authors = ["Ari Lerner"]
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
end
rescue LoadError
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
end
require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
test.libs << 'lib' << 'test'
test.pattern = 'test/**/*_test.rb'
test.verbose = false
end
begin
require 'rcov/rcovtask'
Rcov::RcovTask.new do |test|
test.libs << 'test'
test.pattern = 'test/**/*_test.rb'
test.verbose = true
end
rescue LoadError
task :rcov do
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
end
end
task :default => :test
require 'rake/rdoctask'
Rake::RDocTask.new do |rdoc|
if File.exist?('VERSION.yml')
config = YAML.load(File.read('VERSION.yml'))
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
else
version = ""
end
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "dslify #{version}"
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('lib/**/*.rb')
end
================================================
FILE: vendor/gems/dslify/VERSION.yml
================================================
---
:major: 0
:minor: 1
:patch: 0
================================================
FILE: vendor/gems/dslify/dslify.gemspec
================================================
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = %q{dslify}
s.version = "0.1.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Ari Lerner"]
s.date = %q{2009-05-14}
s.email = %q{arilerner@mac.com}
s.extra_rdoc_files = ["README.rdoc", "LICENSE"]
s.files = ["README.rdoc", "VERSION.yml", "lib/dslify.rb", "test/dslify_test.rb", "test/test_helper.rb", "LICENSE"]
s.has_rdoc = true
s.homepage = %q{http://github.com/auser/dslify}
s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
s.require_paths = ["lib"]
s.rubygems_version = %q{1.3.2}
s.summary = %q{TODO}
if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 3
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
else
end
else
end
end
================================================
FILE: vendor/gems/dslify/lib/dslify.rb
================================================
module Dslify
def self.included(base)
base.send :include, InstanceMethods
base.extend(ClassMethods)
end
module ClassMethods
def default_options(hsh={})
(@_dsl_options ||= {}).merge! hsh
set_default_options(@_dsl_options)
end
def dsl_options
@_dsl_options ||= {}
end
def options
dsl_options
end
def dsl_methods(*syms)
syms.each {|sym| set_default_options({sym => nil}) }
end
def set_default_options(new_options)
new_options.each do |k,v|
dsl_options[k] = v
class_eval define_dsl_method_str(k)
end
end
def define_dsl_method_str(k)
<<-EOE
def #{k}(n=nil)
if n.nil?
fetch(:#{k})
else
self.#{k}=n
end
end
def #{k}=(n)
dsl_options[:#{k}] = n
end
def fetch(k)
dsl_options[k]
end
EOE
end
def inherited(subclass)
subclass.set_default_options(dsl_options)
end
end
module InstanceMethods
def dsl_options
@dsl_options ||= self.class.dsl_options.clone
end
def default_options
Hash[*dsl_options.select{|k,v| self.class.default_options.has_key?(k) }.inject([]){|res,(k,v)| res << k << v }]
end
def set_vars_from_options(hsh={})
hsh.each do |k,v|
instance_eval self.class.define_dsl_method_str(k) unless self.respond_to?(k)
self.send k, v
end
end
def set_default_options(hsh={})
self.class.set_default_options(hsh)
end
def method_missing(m,*a,&block)
if m.to_s[-1..-1] == '?'
t = m.to_s.gsub(/\?/, '').to_sym
warn "DEPRECATED: Dslify will no longer support ? methods. Fix yo code.: #{m}"
respond_to?(t) && !self.send(t, *a, &block).nil?
else
super
end
end
end
end
================================================
FILE: vendor/gems/dslify/test/dslify_test.rb
================================================
require "#{File.dirname(__FILE__)}/test_helper"
class Quickie
include Dslify
def initialize(&block)
instance_eval &block if block
end
end
class QuickieTest < Test::Unit::TestCase
context "setting" do
before do
Quickie.class_eval do
dsl_methods :bank, :snobs, :author, :gilligans_island
end
@q = Quickie.new
end
it "should be able to set methods on self" do
assert_nothing_raised do
@q.bank "bobs"
end
end
it "should set and then retrieve the same value back" do
@q.snobs "are mean"
assert_equal @q.snobs, "are mean"
end
it "should set and retrieve values back with an = sign" do
@q.author = ["Ari Lerner"]
@q.snobs = "Michael"
assert_equal @q.author, ["Ari Lerner"]
assert_equal @q.snobs, "Michael"
end
it "should set these values in the h Hash on the object" do
assert_raise NoMethodError do
@q.movies "can be fun"
end
end
it "should set multiple keys with set_vars_from_options" do
@q.set_vars_from_options({:a => "a", :b => "b"})
assert_equal @q.a, "a"
assert_equal @q.b, "b"
end
it "should set methods even when they are called with a block" do
@q.author Quickie.new do
end
assert_equal @q.author.class, Quickie
end
end
context "calling methods on an instance" do
setup do
class Detective
include Dslify
attr_reader :snooped
def snoop(*n)
@snooped = "done!"
end
end
@d= Detective.new
end
should "Call the method snoop with set_vars_from_options" do
@d.set_vars_from_options(:snoop => true)
assert @d.snooped
end
end
context "default options" do
setup do
class Bang
include Dslify
default_options(
:says => 'vmrun'
)
def initialize(opts={}, &block)
instance_eval &block if block
end
end
@bang = Bang.new
end
should "overwrite the default dsl option in instance_eval" do
assert_equal @bang.says, "vmrun"
@bang = Bang.new do
says "snake"
end
assert_equal @bang.says, "snake"
end
end
context "with inheritance and classes" do
before do
class Pop
include Dslify
default_options :name => "pop", :flavor=>'cherry'
def initialize(o={})
set_vars_from_options(o)
end
def real_method
"the real deal, no magic"
end
end
class Foo < Pop
default_options :name=>'fooey'
end
class Bar < Pop
default_options :name=>'pangy', :taste => "spicy"
end
class Dad < Pop
end
class Grandad < Dad
end
class Defaults < Pop
default_options(
:global_default => "red_rum"
)
end
@pop = Pop.new
@foo = Foo.new
@bar = Bar.new
end
it "should take the default options set on the class" do
assert_equal @pop.dsl_options[:name], "pop"
assert_equal @pop.name, "pop"
end
it "should allow us to add defaults on the instance by calling dsl_options" do
# QuickieTest::Pop.name == "Cinnamon"
@poptart = Pop.new :name => "Cinnamon"
assert_equal @poptart.name, "Cinnamon"
end
it "should take the default options on a second class that inherits from the base" do
assert_equal @foo.name, "fooey"
end
it "should take the default options on a third inheriting class" do
assert_equal @bar.name, "pangy"
end
it "should not add a method not in the default_options" do
assert_equal @bar.respond_to?(:boat), false
end
it "should return the original default options test" do
assert_equal @bar.dsl_options[:taste], "spicy"
assert_equal @bar.dsl_options[:name], "pangy"
end
it "should set the default options of the child to the superclass's if it doesn't exist" do
# QuickieTest::Dad => QuickieTest::Pop
d = Dad.new
assert Pop.new.name == 'pop'
assert_equal "pop", d.name
d.name "Frankenstein"
assert_equal d.name, "Frankenstein"
end
it "should raise if the method isn't found on itself, the parent or in the rest of the method missing chain" do
assert_raise NoMethodError do
Class.new.sanitorium
end
end
it "should be able to reach the grandparent through the chain of dsify-ed classes" do
# QuickieTest::Grandad => QuickieTest::Dad => QuickieTest::Pop
assert Dad.method_defined?(:name)
assert Dad.new.flavor == 'cherry'
assert Grandad.new.name, "pop"
end
it "should grab the default options from the dsl options (instance method)" do
d = Dad.new(:star => "bucks")
assert_equal d.default_options.keys.map {|k| k.to_s }.sort, %w(flavor name)
end
end
context "methods" do
setup do
class MrDanger
include Dslify
default_options :where_to => "The Grand Canyon"
def initialize(o={})
set_vars_from_options(o)
end
def where_to(*a)
"New York City"
end
end
end
should "not override the method where_to" do
assert_equal MrDanger.new.where_to, "New York City"
end
should "not override the method where_to when called with set_vars_from_options" do
assert_equal MrDanger.new(:where_to => "Bank of America").where_to, "New York City"
end
end
context "when calling with a block" do
setup do
class ToddTheSquare
include Dslify
default_options :provider => :vmrun, :t => :nothing
def provider(&block)
instance_eval &block if block
end
end
end
should "should not evaluate the block" do
tts = ToddTheSquare.new
assert_equal tts.t, :nothing
end
should "should evaluate the block" do
tts = ToddTheSquare.new
tts.provider do
self.t = :something
end
assert_equal tts.t, :something
end
end
context "set_vars_from_options" do
setup do
class VarrrrrrrrMatey
include Dslify
default_options :say => "hello", :to => "world"
def initialize(o={}, &block)
set_vars_from_options(o)
instance_eval &block if block
end
def to_s
say + " " + to
end
end
end
should "set the vars on the options with no options" do
assert_equal VarrrrrrrrMatey.new.to_s, "hello world"
end
should "update the options if called with options" do
assert_equal VarrrrrrrrMatey.new({:say => "goodbye"}).to_s, "goodbye world"
end
should "update the options if called with block" do
@v = VarrrrrrrrMatey.new do
to "me"
end
assert_equal @v.to_s, "hello me"
end
end
context "dsl_methods and methods on the object" do
setup do
class PluginMoopieMoop
include Dslify
default_options :stars => to_s
def self.inherited(s)
# stuff
super
end
end
class DoopyDoop < PluginMoopieMoop
def stars
"right now"
end
end
@obj = DoopyDoop.new
end
should "have the method :stars" do
assert @obj.respond_to?(:stars)
end
should "use the method on the instance" do
assert_equal @obj.stars, "right now"
end
end
end
================================================
FILE: vendor/gems/dslify/test/test_helper.rb
================================================
require 'rubygems'
require 'test/unit'
require 'shoulda'
require "context"
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
require 'dslify'
================================================
FILE: vendor/gems/git-style-binaries/.document
================================================
README.rdoc
lib/**/*.rb
bin/*
features/**/*.feature
LICENSE
================================================
FILE: vendor/gems/git-style-binaries/.gitignore
================================================
*.sw?
.DS_Store
coverage
rdoc
pkg
.autosession.vim
.sessions
.session
tags
README.html
================================================
FILE: vendor/gems/git-style-binaries/.gitmodules
================================================
================================================
FILE: vendor/gems/git-style-binaries/README.markdown
================================================
git-style-binaries
==================
Ridiculously easy git-style binaries.
This gem uses [`trollop`](http://trollop.rubyforge.org/) for option parsing
## Installation
gem install jashmenn-git-style-binaries --source=http://gems.github.com
## Screencast
Checkout the new screencast!
## Try it out
cd `gem env gemdir`/gems/jashmenn-git-style-binaries-0.1.4/test/fixtures
./wordpress -h
./wordpress help post
## Goal
Lets use the imaginary `wordpress` gem. Let's say we have three different
actions we want to specify:
* categories
* list
* post
Each command has its own binary in a directory structure like this:
bin/
|-- wordpress
|-- wordpress-categories
|-- wordpress-list
`-- wordpress-post
The goal is to be able to call commands in this manner:
wordpress -h # gives help summary of all commands
wordpress-list -h # gives long help of wordpress-list
wordpress list -h # ditto
echo "about me" | wordpress-post --title="new post" # posts a new post with that title
## Example code
Our `bin/wordpress` binary is called the *primary* . Our primary only needs to contain the following line:
#!/usr/bin/env ruby
require 'git-style-binary/command'
`git-style-binary` will automatically make this command the primary.
The `bin/wordpress-post` binary could contain the following:
#!/usr/bin/env ruby
require 'git-style-binary/command'
GitStyleBinary.command do
short_desc "create a blog post"
banner <<-EOS
Usage: #{command.full_name} #{all_options_string} {content|STDIN}
Posts content to a wordpress blog
EOS
opt :blog, "short name of the blog to use", :default => 'default'
opt :category, "tag/category. specify multiple times for multiple categories", :type => String, :multi => true
opt :title, "title for the post", :required => true, :type => String
opt :type, "type of the content [html|xhtml|text]", :default => 'html', :type => String
run do |command|
command.die :type, "type must be one of [html|xhtml|text]" unless command.opts[:type] =~ /^(x?html|text)$/i
puts "Subcommand name: #{command.name.inspect}"
puts "Options: #{command.opts.inspect}"
puts "Remaining arguments: #{command.argv.inspect}"
end
end
And so on with the other binaries.
## Running the binaries
Now if we run `wordpress -h` we get the following output:
NAME
wordpress
VERSION
0.0.1 (c) 2009 Nate Murray - local
SYNOPSIS
wordpress [--version] [--test-primary] [--help] [--verbose] COMMAND [ARGS]
SUBCOMMANDS
wordpress-categories
do something with categories
wordpress-help
get help for a specific command
wordpress-list
list blog postings
wordpress-post
create a blog post
See 'wordpress help COMMAND' for more information on a specific command.
OPTIONS
-v, --verbose
verbose
-t, --test-primary=
test an option on the primary
-e, --version
Print version and exit
-h, --help
Show this message
Default **options**, **version string**, and **usage banner** are automatically selected for you.
The subcommands and their short descriptions are loaded automatically!
You can pass the `-h` flag to any one of the subcommands (with or without the
connecting `-`) or use the built-in `help` subcommand for the same effect. For instance:
$ wordpress help post
NAME
wordpress-post - create a blog post
VERSION
0.0.1 (c) 2009 Nate Murray - local
SYNOPSIS
wordpress-post [--type] [--version] [--test-primary] [--blog] [--help] [--verbose] [--category]
[--title] COMMAND [ARGS] {content|STDIN}
OPTIONS
-v, --verbose
verbose
-t, --test-primary=
test an option on the primary
-b, --blog=
short name of the blog to use (default: default)
-c, --category=
tag/category. specify multiple times for multiple
categories
-i, --title=
title for the post
-y, --type=
type of the content [html|xhtml|text] (default: html)
-e, --version
Print version and exit
-h, --help
Show this message
For more examples, see the binaries in `test/fixtures/`.
## Primary options
Often you may *want* the primary to have its own set of options. Simply call `GitStyleBinary.primary` with a block like so:
#!/usr/bin/env ruby
require 'git-style-binary/command'
GitStyleBinary.primary do
version "#{command.full_name} 0.0.1 (c) 2009 Nate Murray - local"
opt :test_primary, "a primary string option", :type => String
run do |command|
puts "Primary Options: #{command.opts.inspect}"
end
end
Primary options are **inherited** by all subcommands. That means in this case
all subcommands will now get the `--test-primary` option available to them as
well as this new `version` string.
## Option parsing
Option parsing is done by [trollop](http://trollop.rubyforge.org/).
`git-style-binary` uses this more-or-less exactly. See the [trollop
documentation](http://trollop.rubyforge.org/) for information on how to setup
the options and flags.
## Callbacks
Callbacks are available on the primary and subcommands. Available callbacks currently
are before/after_run. These execute before the run block of the command parser and take
take one argument, which is the command itself
## The `run` block
To get the 'introspection' on the individual binaries every binary is `load`ed
on `primary help`. We need a way to get that information while not running
every command when calling `primary help`. To achieve that you need to put what
will be run in the `run` block.
`run` `yields` a `Command` object which contains a number of useful options
such as `name`, `full_name`, `opts`, and `argv`.
* `command.opts` is a hash of the options parsed
* `command.argv` is an array of the remaining arguments
## Features
* automatic colorization
* automatic paging
## To Learn more
Play with the examples in the `test/fixtures` directory.
## Credits
* `git-style-binary` was written by Nate Murray ``
* `trollop` was written by [William Morgan](http://trollop.rubyforge.org/)
* Inspiration comes from Ari Lerner's [git-style-binaries](http://blog.xnot.org/2008/12/16/git-style-binaries/) for [PoolParty.rb](http://poolpartyrb.com)
* [`colorize.rb`](http://colorize.rubyforge.org) by Michal Kalbarczyk
* Automatic less paging by [Nathan Weizenbaum](http://nex-3.com/posts/73-git-style-automatic-paging-in-ruby)
* Color inspiration from [Brian Henderson](http://xcombinator.com) teaching me how to get `man git` colors using `less` on MacOSX
## TODO
* automagic tab completion - Automatic for subcommands and options for any library that uses this
## Known Bugs/Problems
* Young
* A few places of really ugly code
* A feeling that this could be done in 1/2 lines of code
## Authors
By Nate Murray and Ari Lerner
## Copyright
The MIT License
Copyright (c) 2009 Nate Murray. See LICENSE for details.
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.
================================================
FILE: vendor/gems/git-style-binaries/Rakefile
================================================
require 'rubygems'
require 'rake'
begin
require 'jeweler'
Jeweler::Tasks.new do |gem|
gem.name = "git-style-binaries"
gem.description = %Q{Ridiculously easy git-style binaries}
gem.summary =<<-EOF
Add git-style binaries to your project easily.
EOF
gem.email = "nate@natemurray.com"
gem.homepage = "http://github.com/jashmenn/git-style-binaries"
gem.authors = ["Nate Murray"]
gem.add_dependency 'trollop'
gem.add_dependency 'thoughtbot-shoulda' # for running the tests
excludes = /(README\.html)/
gem.files = (FileList["[A-Z]*.*", "{bin,examples,generators,lib,rails,spec,test,vendor}/**/*", 'Rakefile', 'LICENSE*']).delete_if{|f| f =~ excludes}
gem.extra_rdoc_files = FileList["README*", "ChangeLog*", "LICENSE*"].delete_if{|f| f =~ excludes}
end
rescue LoadError
puts "Jeweler not available. Install it with: sudo gem install jeweler"
end
require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
test.libs << 'lib' << 'test'
test.pattern = 'test/**/*_test.rb'
test.verbose = true
end
begin
require 'rcov/rcovtask'
Rcov::RcovTask.new do |test|
test.libs << 'test'
test.pattern = 'test/**/*_test.rb'
test.verbose = true
end
rescue LoadError
task :rcov do
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
end
end
task :default => :test
require 'rdoc/task'
Rake::RDocTask.new do |rdoc|
if File.exist?('VERSION.yml')
config = YAML.load(File.read('VERSION.yml'))
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
else
version = ""
end
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "git-style-binaries #{version}"
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('lib/**/*.rb')
end
task :bump => ['version:bump:patch', 'gemspec', 'build']
================================================
FILE: vendor/gems/git-style-binaries/VERSION.yml
================================================
---
:patch: 10
:major: 0
:minor: 1
================================================
FILE: vendor/gems/git-style-binaries/doc/EXAMPLES
================================================
see test/fixtures
================================================
FILE: vendor/gems/git-style-binaries/doc/poolparty-binaries.screenplay
================================================
#!/usr/bin/env castanaut
# Castanaut screencast file. sudo gem install castanaut
# see if there is a terminal module
# download the 'ttyrecord' binary
# get castanaut src
# get applescript docs
plugin "terminal"
plugin "keystack"
def cleanup
FileUtils.rm_rf(File.expand_path("~/poolparty-gsb-test/bin"))
end
cleanup
perform "Introduction" do
launch "Terminal", at(10, 10, 800, 600)
pause 1
say <<-eos
Hey, welcome to the git-style-binaries screencast.
First, lets talk a little about what a git-style-binary even is.
eos
end
cli "cd #{ENV['POOLPARTY_SRC']}"
perform "Describing Git Binaries" do
pause 1
while_saying "as you can see here, git has over one hundred binaries that all perform various functions." do
type "git-"
pause 1
keystroke_literal('tab')
keystroke_literal('tab')
type "y"
pause 2
type " "
keystroke_using('u', :control)
end
while_saying "for instance, we have. git-add for adding files. git-status for
getting the status of our repo and so on" do
cli "git-add"
pause 2
cli "git-status"
end
while_saying "one of the nice things about the git-style-binaries is that you
can choose to call the command with or without the dash.. so for instance, i
can call either 'git-add' or 'git add'." do
cli "git-add"
pause 2
cli "git add"
end
perform "Describe Help" do
while_saying "the help in git is also very handy. for instance, if you call
git help it will give you a listing of many of the available subcommands" do
cli "git help"
move to(44, 94)
move to(133, 87)
move to(127, 346)
move to(41, 343)
move to(44, 94)
end
while_saying "you can also ask for help about any of the subcommands. if we
want to get help on 'git-add', we simply type 'git help add'" do
cli "git help add"
type 'q'
end
end
pause 2
perform "Describe Validation" do
while_saying "git also performs option validation, as you would expect. for
instance, if we provide an invalid option such as 'asdf' we get an error" do
cli "git add --asdf"
end
end
while_saying "the goal of the git-style-binaries ruby gem is to bring this
functionality to your own custom binaries in an easy-to-use way. so lets take
a look at an example"
pause 2
end
perform "Introducing PoolParty" do
while_saying "we're going to be using PoolParty's binaries as an example on
how to build git-style-binaries using this gem" do
end
while_saying "just in case you're not familiar with poolparty, its a cloud
management system by Ari Lerner. It allows you to manage your cloud using ruby
code and plugins rather than, say, folders of bash scripts." do
# launch "Safari", at(10, 10, 800, 600)
# url "http://www.poolpartyrb.com" # ?
# pause 3
# keystroke_literal_using('tab', :command) # go back to terminal
end
while_saying "in poolparty the main binary is 'cloud'. we can do cloud help to see all of the available subcommands." do
cli "cloud help"
end
while_saying "as you can see here we have 'cloud-start', for starting our
cloud, 'cloud configure' to reconfigure our cloud after we change something,
even cloud-ssh, to ssh directly into a node of our cloud." do
pause 2
shake 37, 499
pause 2
shake 37, 219
end
pause 2
type "q"
while_saying "what i'd like to do is, rather than just opening up the existing
binaries, Im going to build poolparty-like binaries from the ground up. this
way you can see how to do this on your own."
end
perform "entering poolpary" do
cli "cd ~"
cli "mkdir -p poolparty-gsb-test/bin"
cli "cd poolparty-gsb-test"
end
perform "creating primary" do
while_saying "first we need to create our *primary* binary." do
cli "vim bin/cloud"
pause 2
type_pre <<-eof
i#!/usr/bin/env ruby
require 'git-style-binary/command'
eof
cli ":w"
cli ":e"
end
while_saying "and thats it! thats all we need if we want the default functionality" do
cli ":x"
end
end
pause 1
# opt :cloud_name, "Name of the working cloud", :type => String, :default => nil
perform "create cloud-start" do
while_saying "now lets create cloud-start.
we start by requiring git-style-binary/command, just like last time.
now we open up a #command block.
specify a version.
specify a banner and a short description.
and we put what we want to do in the 'run' block." do
cli "vim bin/cloud-start"
pause 1
vim_insert <<-eof
#!/usr/bin/env ruby
eof
cli ":w"
cli ":e"
cli ":set paste" # no auto indending
type "j"
vim_insert <<-eof
require 'git-style-binary/command'
GitStyleBinary.command do
version "PoolParty cloud-start 0.0.1"
banner <<-\EOS
Usage: cloud-start \#{all_options_string}
EOS
eof
vim_insert <<-eof
short_desc "List the clouds"
run do |command|
puts "Options: \#{command.opts.inspect}"
end
end
eof
end
while_saying "the run block yields a command object. commands has an 'opts' attribute accessor, or you can just use brackets on 'command'" do
type ":0
/puts
Diputs \"verbose is: \#{command[:verbose].to_s}\""
cli ":w"
end
while_saying "you can add more options by calling 'opt' in the command block" do
vim_line_after("short_")
vim_insert " opt :name, \"the name of the cloud you are starting\", :type => String,
:default => \"default\""
end
while_saying "git-style-binaries uses the gem trollop for the option parsing.
trollop allows you to easily add validations and type conversions to options.
see their website for information on the syntax" do
end
# cli ":x"
pause 2
end
perform "running cloud-start" do
while_saying "now lets run cloud-start -h" do
keystroke_using('t', :command) # go back to terminal
pause 1
cli "cd poolparty-gsb-test"
cli "chmod +x bin/*"
cli "./bin/cloud-start -h"
end
pause 2
while_saying "notice a few things here. we've got automatic coloring, a list of all of our flags, and a list of all of our options. trollop has even automatically created the short flags for us" do
move to(39, 178)
pause 1
move to(103, 202)
pause 1
move to(79, 357)
move to(90, 522)
move to(79, 357)
pause 1
move to(203, 299)
move to(470, 300)
pause 1
shake 54, 441
end
pause 3
type "q"
keystroke_using('w', :command) # go back to terminal
pause 2
end
perform "creating cloud-ssh" do
while_saying "i'd like to show you a few more features of gsb. first lets create one more simple binary - cloud ssh" do
cli ":x"
pause 1
cli "vim bin/cloud-ssh"
pause 1
vim_insert <<-eof
#!/usr/bin/env ruby
eof
cli ":w"
cli ":e"
cli ":set paste" # no auto indending
type "j"
vim_insert <<-eof
require 'git-style-binary/command'
GitStyleBinary.command do
version "PoolParty cloud-ssh 0.0.1"
banner <<-\EOS
Usage: cloud-ssh \#{all_options_string}
EOS
eof
vim_insert <<-eof
short_desc "ssh into your cloud"
opt :name, "name of the cloud", :type => String
run do |command|
puts "you are sshing into \#{command[:name]}!"
end
end
eof
pause 1
cli ":x"
pause 2
end
end
perform "show help with both binaries" do
while_saying "now we have two binaries, cloud-start and cloud-ssh" do
cli "tree ."
end
while_saying "one of the cool things about gsb is that it will automatically pick up these files when you run the help commands" do
cli "./bin/cloud -h"
end
while_saying "see, here we have both cloud-start and cloud-ssh, along with their short descriptions, which are automatically loaded from their respective files" do
pause 0.5
shake 46, 401
pause 0.5
shake 49, 338
pause 0.5
move to(88, 291)
move to(354, 297)
pause 1
cli 'q'
end
end
perform "modify cloud primary" do
while_saying "notice that if we do cloud help start. we see that it already
has a number of options. these options come from the default binary options." do
cli "./bin/cloud help start"
pause 1
move to(123, 266)
pause 1
shake 116, 431
pause 1
move to(99, 515)
type 'q'
end
while_saying "gsb's get their options from three places 1) the default in code 2) the primary and 3) the subcommand itself" do
type "1) default options (in code)"
pause 0.5
keystroke_using('u', :control)
type "2) primary (ex: ./bin/cloud)"
pause 0.5
keystroke_using('u', :control)
type "3) subcommand (./bin/cloud-start)"
pause 0.5
keystroke_using('u', :control)
end
while_saying "if we add options to our primary these will be automatically inherited by the subcommands." do
end
while_saying "we want to be able to specify the name of our clouds.rb
specfile in any of the subcommands. so all we need to do is add that to the
primary.
now when we want to customize the primary we need to do a bit more work.
" do
cli "vim bin/cloud"
pause 2
type "jj"
type 'o' + ''
vim_insert <<-eof
GitStyleBinary.primary do
version "PoolParty cloud command"
banner <<-\EOS
eof
vim_insert <<-eof
Usage: cloud \\\#{all_options_string} COMMAND [ARGS]
The cloud subcommands commands are:
\\\#{GitStyleBinary.pretty_known_subcommands(:short).join(" ")}
EOS
opt :spec, "The name of the clouds.rb file", :type => String
run do
end
end
eof
pause 2
type ":x
"
end
end
perform "show subcommand opts" do
while_saying "now if we run the subcommand -h, we see that the spec option is there" do
cli "./bin/cloud-start -h"
pause 1
shake 54, 342
pause 1
type "q"
end
while_saying "you can also specify callbacks in your primary if you want to.
for instance, say you want to call something before the run block of each
subcommand. simply define a before_run block in your primary" do
cli "vim bin/cloud"
pause 2
vim_line_after("opt ")
vim_insert <<-eof
before_run do
puts "this happened before run"
end
eof
pause 2
type ":x
"
end
while_saying "now we run our subcommand again. and you can see that it gets the options!" do
cli "./bin/cloud-start"
end
end
perform "summary" do
while_saying "i hope this has been helpful for you. now go out and create your own git-style-binaries!"
end
at_exit do
cleanup
end
# vim: ft=ruby
================================================
FILE: vendor/gems/git-style-binaries/git-style-binaries.gemspec
================================================
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = %q{git-style-binaries}
s.version = "0.1.10"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Nate Murray"]
s.date = %q{2009-06-12}
s.description = %q{Ridiculously easy git-style binaries}
s.email = %q{nate@natemurray.com}
s.extra_rdoc_files = [
"README.markdown"
]
s.files = [
"README.markdown",
"Rakefile",
"VERSION.yml",
"lib/ext/colorize.rb",
"lib/ext/core.rb",
"lib/git-style-binary.rb",
"lib/git-style-binary/autorunner.rb",
"lib/git-style-binary/command.rb",
"lib/git-style-binary/commands/help.rb",
"lib/git-style-binary/helpers/name_resolver.rb",
"lib/git-style-binary/helpers/pager.rb",
"lib/git-style-binary/parser.rb",
"test/fixtures/flickr",
"test/fixtures/flickr-download",
"test/fixtures/wordpress",
"test/fixtures/wordpress-categories",
"test/fixtures/wordpress-list",
"test/fixtures/wordpress-post",
"test/git-style-binary/command_test.rb",
"test/git_style_binary_test.rb",
"test/running_binaries_test.rb",
"test/shoulda_macros/matching_stdio.rb",
"test/test_helper.rb",
"vendor/gems/trollop/FAQ.txt",
"vendor/gems/trollop/History.txt",
"vendor/gems/trollop/Manifest.txt",
"vendor/gems/trollop/README.txt",
"vendor/gems/trollop/Rakefile",
"vendor/gems/trollop/lib/trollop.rb",
"vendor/gems/trollop/release-script.txt",
"vendor/gems/trollop/test/test_trollop.rb",
"vendor/gems/trollop/www/index.html"
]
s.has_rdoc = true
s.homepage = %q{http://github.com/jashmenn/git-style-binaries}
s.rdoc_options = ["--charset=UTF-8"]
s.require_paths = ["lib"]
s.rubygems_version = %q{1.3.2}
s.summary = %q{Add git-style binaries to your project easily.}
s.test_files = [
"test/git-style-binary/command_test.rb",
"test/git_style_binary_test.rb",
"test/running_binaries_test.rb",
"test/shoulda_macros/matching_stdio.rb",
"test/test_helper.rb"
]
if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 3
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q, [">= 0"])
s.add_runtime_dependency(%q, [">= 0"])
else
s.add_dependency(%q, [">= 0"])
s.add_dependency(%q, [">= 0"])
end
else
s.add_dependency(%q, [">= 0"])
s.add_dependency(%q, [">= 0"])
end
end
================================================
FILE: vendor/gems/git-style-binaries/lib/ext/colorize.rb
================================================
#
# Colorize String class extension.
#
class String
#
# Version string
#
COLORIZE_VERSION = '0.5.6'
#
# Colors Hash
#
COLORS = {
:black => 0,
:red => 1,
:green => 2,
:yellow => 3,
:blue => 4,
:magenta => 5,
:cyan => 6,
:white => 7,
:default => 9,
:light_black => 10,
:light_red => 11,
:light_green => 12,
:light_yellow => 13,
:light_blue => 14,
:light_magenta => 15,
:light_cyan => 16,
:light_white => 17
}
#
# Modes Hash
#
MODES = {
:default => 0, # Turn off all attributes
#:bright => 1, # Set bright mode
:underline => 4, # Set underline mode
:blink => 5, # Set blink mode
:swap => 7, # Exchange foreground and background colors
:hide => 8 # Hide text (foreground color would be the same as background)
}
protected
#
# Set color values in new string intance
#
def set_color_parameters( params )
if (params.instance_of?(Hash))
@color = params[:color]
@background = params[:background]
@mode = params[:mode]
@uncolorized = params[:uncolorized]
self
else
nil
end
end
public
#
# Change color of string
#
# Examples:
#
# puts "This is blue".colorize( :blue )
# puts "This is light blue".colorize( :light_blue )
# puts "This is also blue".colorize( :color => :blue )
# puts "This is blue with red background".colorize( :color => :light_blue, :background => :red )
# puts "This is blue with red background".colorize( :light_blue ).colorize( :background => :red )
# puts "This is blue text on red".blue.on_red
# puts "This is red on blue".colorize( :red ).on_blue
# puts "This is red on blue and underline".colorize( :red ).on_blue.underline
# puts "This is blue text on red".blue.on_red.blink
#
def colorize( params )
unless STDOUT.use_color
return self unless STDOUT.isatty
end
return self if ENV['NO_COLOR']
begin
require 'Win32/Console/ANSI' if RUBY_PLATFORM =~ /win32/
rescue LoadError
raise 'You must gem install win32console to use color on Windows'
end
color_parameters = {}
if (params.instance_of?(Hash))
color_parameters[:color] = COLORS[params[:color]]
color_parameters[:background] = COLORS[params[:background]]
color_parameters[:mode] = MODES[params[:mode]]
elsif (params.instance_of?(Symbol))
color_parameters[:color] = COLORS[params]
end
color_parameters[:color] ||= @color || 9
color_parameters[:background] ||= @background || 9
color_parameters[:mode] ||= @mode || 0
color_parameters[:uncolorized] ||= @uncolorized || self.dup
# calculate bright mode
color_parameters[:color] += 50 if color_parameters[:color] > 10
color_parameters[:background] += 50 if color_parameters[:background] > 10
return "\033[#{color_parameters[:mode]};#{color_parameters[:color]+30};#{color_parameters[:background]+40}m#{color_parameters[:uncolorized]}\033[0m".set_color_parameters( color_parameters )
end
#
# Return uncolorized string
#
def uncolorize
return @uncolorized || self
end
#
# Return true if sting is colorized
#
def colorized?
return !@uncolorized.nil?
end
#
# Make some color and on_color methods
#
COLORS.each_key do | key |
eval <<-"end_eval"
def #{key.to_s}
return self.colorize( :color => :#{key.to_s} )
end
def on_#{key.to_s}
return self.colorize( :background => :#{key.to_s} )
end
end_eval
end
#
# Methods for modes
#
MODES.each_key do | key |
eval <<-"end_eval"
def #{key.to_s}
return self.colorize( :mode => :#{key.to_s} )
end
end_eval
end
class << self
#
# Return array of available modes used by colorize method
#
def modes
keys = []
MODES.each_key do | key |
keys << key
end
keys
end
#
# Return array of available colors used by colorize method
#
def colors
keys = []
COLORS.each_key do | key |
keys << key
end
keys
end
#
# Display color matrix with color names.
#
def color_matrix( txt = "[X]" )
size = String.colors.length
String.colors.each do | color |
String.colors.each do | back |
print txt.colorize( :color => color, :background => back )
end
puts " < #{color}"
end
String.colors.reverse.each_with_index do | back, index |
puts "#{"|".rjust(txt.length)*(size-index)} < #{back}"
end
end
end
end
================================================
FILE: vendor/gems/git-style-binaries/lib/ext/core.rb
================================================
class Object
def returning(value)
yield(value)
value
end unless Object.respond_to?(:returning)
end
class Symbol
def to_proc
Proc.new { |*args| args.shift.__send__(self, *args) }
end
end
class IO
attr_accessor :use_color
end
================================================
FILE: vendor/gems/git-style-binaries/lib/git-style-binary/autorunner.rb
================================================
require 'git-style-binary/parser'
module GitStyleBinary
class AutoRunner
def self.run(argv=ARGV)
r = new
r.run
end
def run
unless GitStyleBinary.run?
if !GitStyleBinary.current_command
GitStyleBinary.load_primary
end
GitStyleBinary.current_command.run
end
end
end
end
================================================
FILE: vendor/gems/git-style-binaries/lib/git-style-binary/command.rb
================================================
require 'git-style-binary'
module GitStyleBinary
def self.command(&block)
returning Command.new(:constraints => [block]) do |c|
c.name ||= (GitStyleBinary.name_of_command_being_loaded || GitStyleBinary.current_command_name)
GitStyleBinary.known_commands[c.name] = c
if !GitStyleBinary.current_command || GitStyleBinary.current_command.is_primary?
GitStyleBinary.current_command = c
end
end
end
def self.primary(&block)
returning Primary.new(:constraints => [block]) do |c|
c.name ||= (GitStyleBinary.name_of_command_being_loaded || GitStyleBinary.current_command_name)
GitStyleBinary.known_commands[c.name] = c
GitStyleBinary.primary_command = c unless GitStyleBinary.primary_command
GitStyleBinary.current_command = c unless GitStyleBinary.current_command
end
end
class Command
class << self
def defaults
lambda do
name_desc "#{command.full_name}\#{command.short_desc ? ' - ' + command.short_desc : ''}" # eval jit
version_string = defined?(VERSION) ? VERSION : "0.0.1"
version "#{version_string} (c) #{Time.now.year}"
banner <<-EOS
#{"SYNOPSIS".colorize(:red)}
#{command.full_name.colorize(:light_blue)} #{all_options_string}
#{"SUBCOMMANDS".colorize(:red)}
\#{GitStyleBinary.pretty_known_subcommands.join("\n ")}
See '#{command.full_name} help COMMAND' for more information on a specific command.
EOS
opt :verbose, "verbose", :default => false
end
end
end
attr_reader :constraints
attr_reader :opts
attr_accessor :name
def initialize(o={})
o.each do |k,v|
eval "@#{k.to_s}= v"
end
end
def parser
@parser ||= begin
p = Parser.new
p.command = self
p
end
end
def constraints
@constraints ||= []
end
def run
GitStyleBinary.load_primary unless is_primary?
GitStyleBinary.load_subcommand if is_primary? && running_subcommand?
load_all_parser_constraints
@opts = process_args_with_subcmd
call_parser_run_block
self
end
def running_subcommand?
GitStyleBinary.valid_subcommand?(GitStyleBinary.current_command_name)
end
def load_all_parser_constraints
@loaded_all_parser_constraints ||= begin
load_parser_default_constraints
load_parser_primary_constraints
load_parser_local_constraints
true
end
end
def load_parser_default_constraints
parser.consume_all([self.class.defaults])
end
def load_parser_primary_constraints
parser.consume_all(GitStyleBinary.primary_command.constraints)
end
def load_parser_local_constraints
cur = GitStyleBinary.current_command # see, why isn't 'this' current_command?
unless self.is_primary? && cur == self
# TODO TODO - the key lies in this function. figure out when you hav emore engergy
# soo UGLY. see #process_parser! unify with that method
# parser.consume_all(constraints) rescue ArgumentError
parser.consume_all(cur.constraints)
end
end
def call_parser_run_block
runs = GitStyleBinary.current_command.parser.runs
parser.run_callbacks(:before_run, self)
parser.runs.last.call(self) # ... not too happy with this
parser.run_callbacks(:after_run, self)
end
def process_args_with_subcmd(args = ARGV, *a, &b)
cmd = GitStyleBinary.current_command_name
vals = process_args(args, *a, &b)
parser.leftovers.shift if parser.leftovers[0] == cmd
vals
end
# TOOooootally ugly! why? bc load_parser_local_constraints doesn't work
# when loading the indivdual commands because it depends on
# #current_command. This really sucks and is UGLY.
# the todo is to put in 'load_all_parser_constraints' and this works
def process_parser!
# load_all_parser_constraints
load_parser_default_constraints
load_parser_primary_constraints
# load_parser_local_constraints
parser.consume_all(constraints)
# hack
parser.consume {
opt :version, "Print version and exit" if @version unless @specs[:version] || @long["version"]
opt :help, "Show this message" unless @specs[:help] || @long["help"]
resolve_default_short_options
} # hack
end
def process_args(args = ARGV, *a, &b)
p = parser
begin
vals = p.parse args
args.clear
p.leftovers.each { |l| args << l }
vals # ugly todo
rescue Trollop::CommandlineError => e
$stderr.puts "Error: #{e.message}."
$stderr.puts "Try --help for help."
exit(-1)
rescue Trollop::HelpNeeded
p.educate
exit
rescue Trollop::VersionNeeded
puts p.version
exit
end
end
def is_primary?
false
end
def argv
parser.leftovers
end
def short_desc
parser.short_desc
end
def full_name
# ugly, should be is_primary?
GitStyleBinary.primary_name == name ? GitStyleBinary.primary_name : GitStyleBinary.primary_name + "-" + name
end
def die arg, msg=nil
p = parser # create local copy
Trollop.instance_eval { @p = p }
Trollop::die(arg, msg)
end
# Helper to return the option
def [](k)
opts[k]
end
end
class Primary < Command
def is_primary?
true
end
def primary
self
end
end
end
================================================
FILE: vendor/gems/git-style-binaries/lib/git-style-binary/commands/help.rb
================================================
module GitStyleBinary
module Commands
class Help
# not loving this syntax, but works for now
GitStyleBinary.command do
short_desc "get help for a specific command"
run do |command|
# this is slightly ugly b/c it has to muck around in the internals to
# get information about commands other than itself. This isn't a
# typical case
def educate_about_command(name)
load_all_commands
if GitStyleBinary.known_commands.has_key?(name)
cmd = GitStyleBinary.known_commands[name]
cmd.process_parser!
cmd.parser.educate
else
puts "Unknown command '#{name}'"
end
end
if command.argv.size > 0
command.argv.first == "help" ? educate : educate_about_command(command.argv.first)
else
educate
end
end
end
end
end
end
================================================
FILE: vendor/gems/git-style-binaries/lib/git-style-binary/helpers/name_resolver.rb
================================================
module GitStyleBinary
module Helpers
module NameResolver
def basename(filename=zero)
File.basename(filename).match(/(.*?)(\-|$)/).captures.first
end
alias_method :primary_name, :basename
# checks the bin directory for all files starting with +basename+ and
# returns an array of strings specifying the subcommands
def subcommand_names(filename=zero)
subfiles = Dir[File.join(binary_directory, basename + "-*")]
cmds = subfiles.collect{|file| File.basename(file).sub(/^#{basename}-/, '')}.sort
cmds += built_in_command_names
cmds.uniq
end
def binary_directory(filename=zero)
File.dirname(filename)
end
def built_in_commands_directory
File.dirname(__FILE__) + "/../commands"
end
def built_in_command_names
Dir[built_in_commands_directory + "/*.rb"].collect{|f| File.basename(f.sub(/\.rb$/,''))}
end
def list_subcommands(filename=zero)
subcommand_names(filename).join(", ")
end
# load first from users binary directory. then load built-in commands if
# available
def binary_filename_for(name)
user_file = File.join(binary_directory, "#{basename}-#{name}")
return user_file if File.exists?(user_file)
built_in = File.join(built_in_commands_directory, "#{name}.rb")
return built_in if File.exists?(built_in)
user_file
end
def current_command_name(filename=zero,argv=ARGV)
current = File.basename(zero)
first_arg = ARGV[0]
return first_arg if valid_subcommand?(first_arg)
return basename if basename == current
current.sub(/^#{basename}-/, '')
end
# returns the command name with the prefix if needed
def full_current_command_name(filename=zero,argv=ARGV)
cur = current_command_name(filename, argv)
subcmd = cur == basename(filename) ? false : true # is this a subcmd?
"%s%s%s" % [basename(filename), subcmd ? "-" : "", subcmd ? current_command_name(filename, argv) : ""]
end
def valid_subcommand?(name)
subcommand_names.include?(name)
end
def zero
$0
end
def pretty_known_subcommands(theme=:long)
GitStyleBinary.known_commands.collect do |k,cmd|
next if k == basename
cmd.process_parser!
("%-s%s%-10s" % [basename, '-', k]).colorize(:light_blue) + ("%s " % [theme == :long ? "\n" : ""]) + ("%s" % [cmd.short_desc]) + "\n"
end.compact.sort
end
end
end
end
================================================
FILE: vendor/gems/git-style-binaries/lib/git-style-binary/helpers/pager.rb
================================================
module GitStyleBinary
module Helpers
module Pager
# by Nathan Weizenbaum - http://nex-3.com/posts/73-git-style-automatic-paging-in-ruby
def run_pager
return if RUBY_PLATFORM =~ /win32/
return unless STDOUT.tty?
STDOUT.use_color = true
read, write = IO.pipe
unless Kernel.fork # Child process
STDOUT.reopen(write)
STDERR.reopen(write) if STDERR.tty?
read.close
write.close
return
end
# Parent process, become pager
STDIN.reopen(read)
read.close
write.close
ENV['LESS'] = 'FSRX' # Don't page if the input is short enough
Kernel.select [STDIN] # Wait until we have input before we start the pager
pager = ENV['PAGER'] || 'less -erXF'
exec pager rescue exec "/bin/sh", "-c", pager
end
module_function :run_pager
end
end
end
================================================
FILE: vendor/gems/git-style-binaries/lib/git-style-binary/parser.rb
================================================
module GitStyleBinary
class Parser < Trollop::Parser
attr_reader :runs, :callbacks
attr_reader :short_desc
attr_accessor :command
def initialize *a, &b
super
@runs = []
setup_callbacks
end
def setup_callbacks
@callbacks = {}
%w(run).each do |event|
%w(before after).each do |time|
@callbacks["#{time}_#{event}".to_sym] = []
instance_eval "def #{time}_#{event}(&block);@callbacks[:#{time}_#{event}] << block;end"
end
end
end
def run_callbacks(at, from)
@callbacks[at].each {|c| c.call(from) }
end
def banner s=nil; @banner = s if s; @banner end
def short_desc s=nil; @short_desc = s if s; @short_desc end
def name_desc s=nil; @name_desc = s if s; @name_desc end
# Set the theme. Valid values are +:short+ or +:long+. Default +:long+
attr_writer :theme
def theme
@theme ||= :long
end
## Adds text to the help display.
def text s; @order << [:text, s] end
def spec_names
@specs.collect{|name, spec| spec[:long]}
end
# should probably be somewhere else
def load_all_commands
GitStyleBinary.subcommand_names.each do |name|
cmd_file = GitStyleBinary.binary_filename_for(name)
GitStyleBinary.load_command_file(name, cmd_file)
end
end
## Print the help message to 'stream'.
def educate(stream=$stdout)
load_all_commands
width # just calculate it now; otherwise we have to be careful not to
# call this unless the cursor's at the beginning of a line.
GitStyleBinary::Helpers::Pager.run_pager
self.send("educate_#{theme}", stream)
end
def educate_long(stream=$stdout)
left = {}
@specs.each do |name, spec|
left[name] =
((spec[:short] ? "-#{spec[:short]}, " : "") +
"--#{spec[:long]}" +
case spec[:type]
when :flag; ""
when :int; "="
when :ints; "="
when :string; "="
when :strings; "="
when :float; "="
when :floats; "="
end).colorize(:red)
end
leftcol_width = left.values.map { |s| s.length }.max || 0
rightcol_start = leftcol_width + 6 # spaces
leftcol_start = 6
leftcol_spaces = " " * leftcol_start
unless @order.size > 0 && @order.first.first == :text
if @name_desc
stream.puts "NAME".colorize(:red)
stream.puts "#{leftcol_spaces}"+ colorize_known_words(eval(%Q["#{@name_desc}"])) + "\n"
stream.puts
end
if @version
stream.puts "VERSION".colorize(:red)
stream.puts "#{leftcol_spaces}#@version\n"
end
stream.puts
banner = colorize_known_words_array(wrap(eval(%Q["#{@banner}"]) + "\n", :prefix => leftcol_start)) if @banner # lazy banner
stream.puts banner
stream.puts
stream.puts "OPTIONS".colorize(:red)
else
stream.puts "#@banner\n" if @banner
end
@order.each do |what, opt|
if what == :text
stream.puts wrap(opt)
next
end
spec = @specs[opt]
stream.printf " %-#{leftcol_width}s\n", left[opt]
desc = spec[:desc] +
if spec[:default]
if spec[:desc] =~ /\.$/
" (Default: #{spec[:default]})"
else
" (default: #{spec[:default]})"
end
else
""
end
stream.puts wrap(" %s" % [desc], :prefix => leftcol_start, :width => width - rightcol_start - 1 )
stream.puts
stream.puts
end
end
def educate_short(stream=$stdout)
left = {}
@specs.each do |name, spec|
left[name] = "--#{spec[:long]}" +
(spec[:short] ? ", -#{spec[:short]}" : "") +
case spec[:type]
when :flag; ""
when :int; " "
when :ints; " "
when :string; " "
when :strings; " "
when :float; " "
when :floats; " "
end
end
leftcol_width = left.values.map { |s| s.length }.max || 0
rightcol_start = leftcol_width + 6 # spaces
leftcol_start = 0
unless @order.size > 0 && @order.first.first == :text
stream.puts "#@version\n" if @version
stream.puts colorize_known_words_array(wrap(eval(%Q["#{@banner}"]) + "\n", :prefix => leftcol_start)) if @banner # jit banner
stream.puts "Options:"
else
stream.puts "#@banner\n" if @banner
end
@order.each do |what, opt|
if what == :text
stream.puts wrap(opt)
next
end
spec = @specs[opt]
stream.printf " %#{leftcol_width}s: ", left[opt]
desc = spec[:desc] +
if spec[:default]
if spec[:desc] =~ /\.$/
" (Default: #{spec[:default]})"
else
" (default: #{spec[:default]})"
end
else
""
end
stream.puts wrap(desc, :width => width - rightcol_start - 1, :prefix => rightcol_start)
end
end
def colorize_known_words_array(txts)
txts.collect{|txt| colorize_known_words(txt)}
end
def colorize_known_words(txt)
txt = txt.gsub(/^([A-Z]+\s*)$/, '\1'.colorize(:red)) # all caps words on their own line
txt = txt.gsub(/\b(#{bin_name})\b/, '\1'.colorize(:light_blue)) # the current command name
txt = txt.gsub(/\[([^\s]+)\]/, "[".colorize(:magenta) + '\1'.colorize(:green) + "]".colorize(:magenta)) # synopsis options
end
def consume(&block)
cloaker(&block).bind(self).call
end
def consume_all(blocks)
blocks.each {|b| consume(&b)}
end
def bin_name
GitStyleBinary.full_current_command_name
end
def all_options_string
# '#{spec_names.collect(&:to_s).collect{|name| "[".colorize(:magenta) + "--" + name + "]".colorize(:magenta)}.join(" ")} COMMAND [ARGS]'
'#{spec_names.collect(&:to_s).collect{|name| "[" + "--" + name + "]"}.join(" ")} COMMAND [ARGS]'
end
def run(&block)
@runs << block
end
def action(name = :action, &block)
block.call(self) if block
end
end
end
================================================
FILE: vendor/gems/git-style-binaries/lib/git-style-binary.rb
================================================
$:.unshift(File.dirname(__FILE__))
require 'rubygems'
# Load the vendor gems
$:.unshift(File.dirname(__FILE__) + "/../vendor/gems")
%w(trollop).each do |library|
begin
require "#{library}/lib/#{library}"
rescue LoadError
begin
require 'trollop'
rescue LoadError
puts "There was an error loading #{library}. Try running git submodule init && git submodule update to correct the problem"
end
end
end
require 'ext/core'
require 'ext/colorize'
require 'git-style-binary/autorunner'
Dir[File.dirname(__FILE__) + "/git-style-binary/helpers/*.rb"].each {|f| require f}
module GitStyleBinary
class << self
include Helpers::NameResolver
attr_accessor :current_command
attr_accessor :primary_command
attr_writer :known_commands
# If set to false GitStyleBinary will not automatically run at exit.
attr_writer :run
# Automatically run at exit?
def run?
@run ||= false
end
def parser
@p ||= Parser.new
end
def known_commands
@known_commands ||= {}
end
def load_primary
unless @loaded_primary
@loaded_primary = true
primary_file = File.join(binary_directory, basename)
load primary_file
if !GitStyleBinary.primary_command # you still dont have a primary load a default
GitStyleBinary.primary do
run do |command|
educate
end
end
end
end
end
def load_subcommand
unless @loaded_subcommand
@loaded_subcommand = true
cmd_file = GitStyleBinary.binary_filename_for(GitStyleBinary.current_command_name)
load cmd_file
end
end
def load_command_file(name, file)
self.name_of_command_being_loaded = name
load file
self.name_of_command_being_loaded = nil
end
# UGLY eek
attr_accessor :name_of_command_being_loaded
end
end
at_exit do
unless $! || GitStyleBinary.run?
command = GitStyleBinary::AutoRunner.run
exit 0
end
end
================================================
FILE: vendor/gems/git-style-binaries/test/fixtures/flickr
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../../lib")
VERSION="0.0.2" # just to test it
require 'git-style-binary/command'
================================================
FILE: vendor/gems/git-style-binaries/test/fixtures/flickr-download
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../../lib")
require 'git-style-binary/command'
GitStyleBinary.command do
short_desc "download a flickr image"
banner <<-EOS
SYNOPSIS
#{command.full_name} #{all_options_string} url
Downloads an image from flickr
EOS
run do |command|
puts "would download: #{command.argv.inspect}"
end
end
================================================
FILE: vendor/gems/git-style-binaries/test/fixtures/wordpress
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../../lib")
require 'git-style-binary/command'
GitStyleBinary.primary do
version "0.0.1 (c) 2009 Nate Murray - local"
opt :test_primary, "test an option on the primary", :type => String
action do
@categories = ["sports", "news"]
end
before_run do |cmd|
puts "before_run command #{cmd}"
end
after_run do |cmd|
puts "after_run command #{cmd}"
end
run do |command|
puts "Primary Options: #{command.opts.inspect}"
end
end
# OR
# require 'git-style-binary/primary'
# command = GitStyleBinary::primary("wordpress") do
# version "#{$0} 0.0.1 (c) 2009 Nate Murray"
# banner <<-EOS
# usage: #{$0} #{all_options.collect(:&to_s).join(" ")} COMMAND [ARGS]
#
# The wordpress subcommands commands are:
# {subcommand_names.pretty_print}
#
# See 'wordpress help COMMAND' for more information on a specific command.
# EOS
# opt :verbose, "verbose", :default => false
# opt :dry, "dry run", :default => false
# opt :test_global, "a basic global string option", :type => String
# end
================================================
FILE: vendor/gems/git-style-binaries/test/fixtures/wordpress-categories
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../../lib")
require 'git-style-binary/command'
GitStyleBinary.command do
short_desc "do something with categories"
banner <<-EOS
SYNOPSIS
#{command.full_name} #{all_options_string}
Does something with categories
EOS
run do |command|
puts "does something with categories"
puts @categories.join(" ")
end
end
================================================
FILE: vendor/gems/git-style-binaries/test/fixtures/wordpress-list
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../../lib")
require 'git-style-binary/command'
GitStyleBinary.command do
short_desc "list blog postings"
banner <<-EOS
SYNOPSIS
#{command.full_name} #{all_options_string}
Lists the posts on the blog
EOS
run do |command|
puts "listing blog posts"
end
end
================================================
FILE: vendor/gems/git-style-binaries/test/fixtures/wordpress-post
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + "/../../lib")
require 'git-style-binary/command'
GitStyleBinary.command do
short_desc "create a blog post"
banner <<-EOS
SYNOPSIS
#{command.full_name} #{all_options_string} {content|STDIN}
EOS
opt :blog, "short name of the blog to use", :default => 'default'
opt :category, "tag/category. specify multiple times for multiple categories", :type => String, :multi => true
opt :title, "title for the post", :required => true, :type => String
opt :type, "type of the content [html|xhtml|text]", :default => 'html', :type => String
run do |command|
command.die :type, "type must be one of [html|xhtml|text]" unless command.opts[:type] =~ /^(x?html|text)$/i
puts "Subcommand name: #{command.name.inspect}"
puts "Options: #{command.opts.inspect}"
puts "Remaining arguments: #{command.argv.inspect}"
end
end
================================================
FILE: vendor/gems/git-style-binaries/test/git-style-binary/command_test.rb
================================================
require File.dirname(__FILE__) + "/../test_helper.rb"
require 'git-style-binary/command'
class CommandTest < Test::Unit::TestCase
context "cmd" do
setup do
@c = GitStyleBinary::Command.new
end
should "be able to easily work with constraints" do
assert_equal @c.constraints, []
@c.constraints << "foo"
assert_equal @c.constraints, ["foo"]
end
end
end
================================================
FILE: vendor/gems/git-style-binaries/test/git_style_binary_test.rb
================================================
require File.dirname(__FILE__) + "/test_helper.rb"
class GitStyleBinariesTest < Test::Unit::TestCase
context "parsing basenames" do
should "accurately parse basenames" do
assert_equal "wordpress", GitStyleBinary.basename("bin/wordpress")
assert_equal "wordpress", GitStyleBinary.basename("bin/wordpress-post")
assert_equal "wordpress", GitStyleBinary.basename("wordpress-post")
end
should "get the current command name" do
# doesn't really apply any more b/c it calls 'current' which is never the
# current when your running rake_test_loader.rb
#
# assert_equal "wordpress", GitStyleBinary.current_command_name("bin/wordpress", ["--help"])
# assert_equal "post", GitStyleBinary.current_command_name("bin/wordpress-post", ["--help"])
# assert_equal "post", GitStyleBinary.current_command_name("bin/wordpress post", ["--help"])
#assert_equal "post", GitStyleBinary.current_command_name("bin/wordpress post", [])
end
end
end
================================================
FILE: vendor/gems/git-style-binaries/test/running_binaries_test.rb
================================================
require File.dirname(__FILE__) + "/test_helper.rb"
THIS_YEAR=Time.now.year # todo
class RunningBinariesTest < Test::Unit::TestCase
include RunsBinaryFixtures
context "when running primary" do
["wordpress -h", "wordpress help"].each do |format|
context "and getting help as a '#{format}'" do
setup { @stdout, @stderr = bin(format) }
should "have the command name and short description" do
unless format == "wordpress -h" # doesn't apply to wordpress -h
output_matches /NAME\n\s*wordpress\-help \- get help for a specific command/m
end
end
should "have a local (not default) version string" do
output_matches /0\.0\.1 \(c\) 2009 Nate Murray - local/
end
should "get a list of subcommands" do
output_matches /subcommands/mi
end
should "have subcommand short descriptions" do
output_matches /post\s*create a blog post/
output_matches /categories\s*do something with categories/
output_matches /help\s*get help for a specific command/
output_matches /list\s*list blog postings/
end
should "have a usage" do
output_matches /SYNOPSIS/i
output_matches /wordpress(\-help)? \[/
end
should "be able to ask for help about help"
end
end
context "and getting help as subcommand" do
# ["wordpress -h", "wordpress help"].each do |format|
["wordpress help"].each do |format|
context "'#{format}'" do
should "get help on subcommand post"
end
end
end
context "with no options" do
setup { @stdout, @stderr = bin("wordpress") }
should "output the options" do
output_matches /Primary Options:/
end
should "have the test_primary option" do
output_matches /test_primary=>nil/
end
end
should "be able to require 'primary' and run just fine"
end
context "when running with an action" do
# should be the same for both formats
["wordpress-categories", "wordpress categories"].each do |bin_format|
context "#{bin_format}" do
context "with action block" do
setup { @stdout, @stderr = bin("#{bin_format}") }
should "have the parsed action items in the help output" do
output_matches /sports news/m
end
end
end
end
end
context "callbacks" do
context "on a binary" do
setup { @stdout, @stderr = bin("wordpress") }
%w(before after).each do |time|
should "run the callback #{time}_run}" do
assert @stdout.match(/#{time}_run command/)
end
end
end
context "on help" do
setup { @stdout, @stderr = bin("wordpress -h") }
%w(before after).each do |time|
should "not run the callback #{time}_run" do
assert_nil @stdout.match(/#{time}_run command/)
end
end
end
end
context "when running the subcommand" do
# should be the same for both formats
["wordpress-post", "wordpress post"].each do |bin_format|
context "#{bin_format}" do
context "with no options" do
setup { @stdout, @stderr = bin("#{bin_format}") }
should "fail because title is required" do
output_matches /Error: option 'title' must be specified.\s*Try --help for help/m
end
end
context "with options" do
setup { @stdout, @stderr = bin("#{bin_format} --title='glendale'") }
should "be running the subcommand's run block" do
output_matches /Subcommand name/
end
should "have some default options" do
output_matches /version=>false/
output_matches /help=>false/
end
should "have some primary options" do
output_matches /test_primary=>nil/
end
should "have some local options" do
output_matches /title=>"glendale"/
output_matches /type=>"html"/
end
end
context "testing die statements" do
setup { @stdout, @stderr = bin("#{bin_format} --title='glendale' --type=yaml") }
should "die on invalid options" do
output_matches /argument \-\-type type must be one of \[html\|xhtml\|text\]/
end
end
end # end bin_format
end # end #each
end
["wordpress help post", "wordpress post -h"].each do |format|
context "when calling '#{format}'" do
setup { @stdout, @stderr = bin(format) }
should "have a description" do
output_matches /create a blog post/
end
should "have the proper usage line" do
output_matches /SYNOPSIS\n\s*wordpress\-post/m
output_matches /\[--title\]/
end
should "have option flags" do
output_matches /\-\-title(.*)/
end
should "have primary option flags" do
output_matches /\-\-test-primary(.*)/
end
should "have default option flags" do
output_matches /\-\-verbose/
end
should "have trollop default option flags" do
output_matches /\-e, \-\-version/
end
should "have the correct binary name and short description" do
output_matches /NAME\n\s*wordpress\-post \- create a blog post/m
end
should "have a the primaries version string" do
output_matches /0\.0\.1 \(c\) 2009 Nate Murray - local/
end
should "have options" do
output_matches /Options/i
output_matches /\-b, \-\-blog=/
output_matches /short name of the blog to use/
output_matches /-i, \-\-title=/
output_matches /title for the post/
end
end
end
context "when running a bare primary" do
["flickr -h", "flickr help"].each do |format|
context format do
setup { @stdout, @stderr = bin(format) }
should "have the name and short description" do
unless format == "flickr -h" # hmm
output_matches /NAME\n\s*flickr\-help \- get help for a specific command/m
end
end
should "have a local (not default) version string" do
output_matches /0\.0\.2 \(c\) 2009/
end
end
end
["flickr-download -h", "flickr download -h"].each do |format|
context format do
setup { @stdout, @stderr = bin(format) }
should "match on usage" do
output_matches /SYNOPSIS\n\s*flickr\-download/m
end
end
end
end
end
================================================
FILE: vendor/gems/git-style-binaries/test/shoulda_macros/matching_stdio.rb
================================================
class Test::Unit::TestCase
def output_should_match(regexp)
assert_match regexp, @stdout + @stderr
end
alias_method :output_matches, :output_should_match
def stdout_should_match(regexp)
assert_match regexp, @stdout
end
def stderr_should_match(regexp)
assert_match regexp, @stderr
end
end
================================================
FILE: vendor/gems/git-style-binaries/test/test_helper.rb
================================================
require 'rubygems'
require 'test/unit'
require 'shoulda'
begin require 'redgreen'; rescue LoadError; end
require 'open3'
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
$LOAD_PATH.unshift(File.dirname(__FILE__))
Dir[File.join(File.dirname(__FILE__), "shoulda_macros", "*.rb")].each {|f| require f}
ENV['NO_COLOR'] = "true"
require 'git-style-binary'
GitStyleBinary.run = true
class Test::Unit::TestCase
def FIXTURES_PATH
File.join(File.dirname(__FILE__), "fixtures")
end
end
module RunsBinaryFixtures
# run the specified cmd returning the string values of [stdout,stderr]
def bin(cmd)
stdin, stdout, stderr = Open3.popen3("#{FIXTURES_PATH}/#{cmd}")
[stdout.read, stderr.read]
end
end
================================================
FILE: vendor/gems/searchable_paths/.document
================================================
README.rdoc
lib/**/*.rb
bin/*
features/**/*.feature
LICENSE
================================================
FILE: vendor/gems/searchable_paths/.gitignore
================================================
*.sw?
.DS_Store
coverage
rdoc
pkg
================================================
FILE: vendor/gems/searchable_paths/LICENSE
================================================
Copyright (c) 2009 Ari Lerner
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.
================================================
FILE: vendor/gems/searchable_paths/README.rdoc
================================================
= searchable_paths
Searchable paths
== Note on Patches/Pull Requests
* Fork the project.
* Make your feature addition or bug fix.
* Add tests for it. This is important so I don't break it in a
future version unintentionally.
* Commit, do not mess with rakefile, version, or history.
(if you want to have your own version, that is fine but
bump version in a commit by itself I can ignore when I pull)
* Send me a pull request. Bonus points for topic branches.
== Copyright
Copyright (c) 2009 Nate Murray, Ari Lerner. See LICENSE for details.
================================================
FILE: vendor/gems/searchable_paths/Rakefile
================================================
require 'rubygems'
require 'rake'
begin
require 'jeweler'
Jeweler::Tasks.new do |gem|
gem.name = "searchable_paths"
gem.summary = %Q{Add path searching}
gem.description = %Q{Add searchable paths to any class}
gem.email = "arilerner@mac.com"
gem.homepage = "http://github.com/auser/searchable_paths"
gem.authors = ["Nate Murray and Ari Lerner"]
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
end
Jeweler::GemcutterTasks.new
rescue LoadError
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
end
require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
test.libs << 'lib' << 'test'
test.pattern = 'test/**/*_test.rb'
test.verbose = true
end
begin
require 'rcov/rcovtask'
Rcov::RcovTask.new do |test|
test.libs << 'test'
test.pattern = 'test/**/*_test.rb'
test.verbose = true
end
rescue LoadError
task :rcov do
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
end
end
task :test => :check_dependencies
task :default => :test
require 'rake/rdoctask'
Rake::RDocTask.new do |rdoc|
if File.exist?('VERSION')
version = File.read('VERSION')
else
version = ""
end
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "searchable_paths #{version}"
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('lib/**/*.rb')
end
================================================
FILE: vendor/gems/searchable_paths/lib/searchable_paths.rb
================================================
=begin rdoc
=end
module SearchablePaths
def self.included(mod)
mod.extend(ClassMethods)
end
module ClassMethods
# Specify that a particular class has methods for searchable paths.
#
# Options:
# * :dirs: array of directories to look in *under* the search paths. (default: ["/"])
# * :dir: set the directory to look in *under* the search paths. Use either dir or dirs, not both. (default: +/+)
# * :paths: overwrite all default paths and set the paths to this array exactly
# * :append_paths: append these paths to any existing paths
# * :prepend_paths: prepend these paths to any existing paths
def has_searchable_paths(opts={})
class_eval do
@searchable_paths_dirs = [opts[:dir]] if opts[:dir]
@searchable_paths_dirs = opts[:dirs] if opts[:dirs]
@paths_override = opts[:paths] if opts[:paths]
@paths_prepend = opts[:prepend_paths] || []
@paths_append = opts[:append_paths] || []
end
extend SearchablePaths::SingletonMethods
include SearchablePaths::InstanceMethods
end
def searchable_paths_dir; @searchable_paths_dirs.first; end
def searchable_paths_dirs
@searchable_paths_dirs && @searchable_paths_dirs.size > 0 ? @searchable_paths_dirs : ["/"]
end
# These are the default search paths in order:
#
# * current working directory (Dir.pwd)
def default_paths
[
Dir.pwd
]
end
# returns the full set of valid searchable paths, given the options
def searchable_paths
return @paths_override if @paths_override && @paths_override.size > 0
@searchable_paths ||= @paths_prepend + default_paths + @paths_append
end
end
module SingletonMethods
end
module InstanceMethods
# Searches for +filepath+ in the searchable_paths iff +filepath+
# doesn't exist. e.g. +filepath+ is interpreted *first* as an absolute
# path, if +filepath+ doesn't exist verbatim then it looks for the file
# in the searchable_paths.
#
# Returns +nil+ if the file cannot be found.
def search_in_known_locations(filepath, additional_search_paths=[])
return filepath if File.exists?(filepath) # return the file if its an absolute path
additional_search_paths.each do |path|
full_path = File.expand_path(path / filepath)
return full_path if File.exists?(full_path)
end
self.class.searchable_paths.each do |path|
self.class.searchable_paths_dirs.each do |dir|
next if path.nil?
full_path = File.expand_path(path / dir / filepath)
return full_path if File.exists?(full_path)
end
end
nil
end
alias_method :find_file, :search_in_known_locations
end
end
================================================
FILE: vendor/gems/searchable_paths/test/searchable_paths_test.rb
================================================
require "test_helper"
PATH_TEST_ROOT = "/tmp/poolparty" / "path_test"
PATH_ONE = PATH_TEST_ROOT / "one"
PATH_TWO = PATH_TEST_ROOT / "two"
class TestFile
include SearchablePaths
has_searchable_paths(:dir => "templates", :paths => [PATH_ONE, PATH_TWO])
end
class TestFile2
include SearchablePaths
has_searchable_paths(:dirs => ["clouds", "/"], :prepend_paths => [PATH_ONE, PATH_TWO])
end
def File.write_to_file(name, content="")
open(name, "w") {|f| f.print content}
end
class SearchablePathsTest < Test::Unit::TestCase
def setup
# create path one
[PATH_ONE/:clouds, PATH_ONE/:templates, PATH_ONE/:foo].each {|dir| FileUtils.mkdir_p(dir) }
[PATH_ONE/'clouds.rb', PATH_ONE/:clouds/'special.rb', PATH_ONE/:templates/'apache.conf'].each do |f|
File.write_to_file(f)
end
# create path two
[PATH_TWO/:clouds, PATH_TWO/:templates, PATH_TWO/:foo].each {|dir| FileUtils.mkdir_p(dir) }
[PATH_TWO/'swing.rb', PATH_TWO/'clouds.rb', PATH_TWO/:clouds/'common.rb', PATH_TWO/:templates/'mysql.conf'].each do |f|
File.write_to_file(f)
end
@template = TestFile.new
@cloud = TestFile2.new
end
def teardown
FileUtils.rm_rf(PATH_TEST_ROOT)
end
def test_should_have_class_options_set
assert_equal "templates", TestFile.searchable_paths_dir
assert_equal ["templates"], TestFile.searchable_paths_dirs
assert_equal ["clouds", "/"], TestFile2.searchable_paths_dirs
end
def test_should_be_able_to_find_a_template
assert_not_nil @template.find_file("apache.conf")
assert_equal PATH_ONE/:templates/'apache.conf', @template.find_file("apache.conf")
assert_equal PATH_TWO/:templates/'mysql.conf', @template.find_file("mysql.conf")
end
def test_shouldnt_find_a_template_that_doesnt_exist
assert_nil @template.find_file("post-office.conf")
end
def test_should_find_something_with_prepended_paths
assert_equal PATH_ONE/'clouds.rb', @cloud.find_file("clouds.rb")
assert_equal PATH_TWO/'swing.rb', @cloud.find_file("swing.rb")
end
def test_should_find_things_in_the_right_order
assert_equal PATH_ONE/'clouds.rb', @cloud.find_file("clouds.rb")
end
def test_should_look_in_the_additional_search_paths_first
FileUtils.mkdir_p(PATH_ONE/'extra')
File.write_to_file(PATH_ONE/'extra'/'clouds.rb')
assert_equal @cloud.find_file("clouds.rb", [PATH_ONE/'extra']), PATH_ONE/'extra'/'clouds.rb'
end
end
================================================
FILE: vendor/gems/searchable_paths/test/test_helper.rb
================================================
require 'rubygems'
require 'test/unit'
require "fileutils"
$LOAD_PATH.unshift(File.dirname(__FILE__))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
require 'searchable_paths'
class Test::Unit::TestCase
end
class String
def /(o)
File.join(self, o.to_s)
end
end
================================================
FILE: vendor/gems/trollop/FAQ.txt
================================================
Trollop FAQ
-----------
Q: Why is it called "Trollop"?
A: No reason.
Q: Why should I use Trollop?
A: Because it will take you FEWER LINES OF CODE to do reasonable option parsing
than any other option parser out there.
Look at this:
opts = Trollop::options do
opt :monkey, "Use monkey mode"
opt :goat, "Use goat mode", :default => true
opt :num_limbs, "Set number of limbs", :default => 4
end
That's it. And opts is a hash and you do whatever you want with it.
Trivial. You don't have to mix option processing code blocks with the
declarations. You don't have to make a class for every option (what is this,
Java?). You don't have to write more than 1 line of code per option.
Plus, you get a beautiful help screen that detects your terminal width and
wraps appropriately. C'mon, that's hot.
Q: What is the philosophy behind Trollop?
A: Must a commandline option processor have a philosophy?
Q: Seriously now. What is it?
A: Ok, it's this: Trollop *just* does the parsing and gives you a hash table
of the result. So whatever fancy logic or constraints you need, you can
implement by operating on that hash table. Options that disable other
options, fancy constraints involving multiple sets of values across multiple
sets of options, etc. are all left for you to do manually.
(Trollop does support limited constraint setting (see #conflicts and
#depends), but any non-trivial program will need to get fancier.)
The result is that using Trollop is pretty simple, and whatever bizarre
logic you want, you can write yourself. And don't forget, you can call
Trollop::die to abort the program and give a fancy options-related error
message.
Q: What happens to the other stuff on the commandline?
A: Anything Trollop doesn't recognize as an option or as an option parameter is
left in ARGV for you to process.
Q: Does Trollop support multiple-value arguments?
A: Yes. If you set the :type of an option to something plural, like ":ints",
":strings", ":doubles", ":floats", ":ios", it will accept multiple arguments
on the commandline and the value will be an array of these.
Q: Does Trollop support arguments that can be given multiple times?
A: Yes. If you set :multi to true, then the argument can appear multiple times
on the commandline, and the value will be an array of the parameters.
Q: Does Trollop support subcommands?
A: Yes. You get subcommand support by adding a call #stop_on within the options
block, and passing the names of the subcommands to it. (See the third
example on the webpage.) When Trollop encounters one of these subcommands on
the commandline, it stops processing and returns.
ARGV at that point will contain the subcommand followed by any subcommand
options, since Trollop has contained the rest. So you can consume the
subcommand and call Trollop.options again with the particular options set
for that subcommand.
If you don't know the subcommands ahead of time, you can call
#stop_on_unknown, which will cause Trollop to stop when it encounters any
unknown token. This might be more trouble than its worth if you're also
passing filenames on the commandline.
It's probably easier to see the example on the webpage than to read all
that.
Q: Why does Trollop disallow numeric short argument names, like '-1' and '-9'?
A: Because it's ambiguous whether these are arguments or negative integer or
floating-point parameters to arguments. E.g., what about "-f -3". Is that a
negative three parameter to -f, or two separate parameters?
I could be very clever and detect when there are no arguments that require
floating-point parameters, and allow such short option names in those cases,
but opted for simplicity and consistency.
================================================
FILE: vendor/gems/trollop/History.txt
================================================
== 1.14 / 2009-06-19
* Make :multi arguments default to [], not nil, when not set on the commandline.
* Minor commenting and error message improvements
== 1.13 / 2009-03-16
* Fix parsing of "--longarg=".
== 1.12 / 2009-01-30
* Fix some unit test failures in the last release. Should be more careful.
* Make default short options only be assigned *after* all user-specified
short options. Now there's a little less juggling to do when you just
want to specify a few short options.
== 1.11 / 2009-01-29
* Set _given keys in the results hash for options that were specified
on the commandline.
== 1.10.2 / 2008-10-23
* No longer try `stty size` for screen size detection. Just use curses, and
screen users will have to deal with the screen clearing.
== 1.10.1 / 2008-10-22
* Options hash now responds to method calls as well as standard hash lookup.
* Default values for multi-occurrence parameters now autoboxed.
* The relationship between multi-value, multi-occurrence, and default values
improved and explained.
* Documentation improvements.
== 1.10 / 2008-10-21
* Added :io type for parameters that point to IO streams (filenames, URIs, etc).
* For screen size detection, first try `stty size` before loading Curses.
* Improved documentation.
== 1.9 / 2008-08-20
* Added 'stop_on_unknown' command to stop parsing on any unknown argument.
This is useful for handling sub-commands when you don't know the entire
set of commands up front. (E.g. if the initial arguments can change it.)
* Added a :multi option for parameters, signifying that they can be specified
multiple times.
* Added :ints, :strings, :doubles, and :floats option types, which can take
multiple arguments.
== 1.8.2 / 2008-06-25
* Bugfix for #conflicts and #depends error messages
== 1.8.1 / 2008-06-24
* Bugfix for short option autocreation
* More aggressive documentation
== 1.8 / 2008-06-16
* Sub-command support via Parser#stop_on
== 1.7.2 / 2008-01-16
* Ruby 1.9-ify. Apparently this means replacing :'s with ;'s.
== 1.7.1 / 2008-01-07
* Documentation improvements
== 1.7 / 2007-06-17
* Fix incorrect error message for multiple missing required arguments
(thanks to Neill Zero)
== 1.6 / 2007-04-01
* Don't attempt curses screen-width magic unless running on a terminal.
== 1.5 / 2007-03-31
* --help and --version do the right thing even if the rest of the
command line is incorrect.
* Added #conflicts and #depends to model dependencies and exclusivity
between arguments.
* Minor bugfixes.
== 1.4 / 2007-03-26
* Disable short options with :short => :none.
* Minor bugfixes and error message improvements.
== 1.3 / 2007-01-31
* Wrap at (screen width - 1) instead of screen width.
* User can override --help and --version.
* Bugfix in handling of -v and -h.
* More tests to confirm the above.
== 1.2 / 2007-01-31
* Minor documentation tweaks.
* Removed hoe dependency.
== 1.1 / 2007-01-30
* Trollop::options now passes any arguments as block arguments. Since
instance variables are not properly captured by the block, this
makes it slightly less noisy to pass them in as local variables.
(A real-life use for _why's cloaker!)
* Help display now preserves original argument order.
* Trollop::die now also has a single string form in case death is not
due to a single argument.
* Parser#text now an alias for Parser#banner, and can be called
multiple times, with the output being placed in the right position
in the help text.
* Slightly more indicative formatting for parameterized arguments.
== 1.0 / 2007-01-29
* Initial release.
================================================
FILE: vendor/gems/trollop/Manifest.txt
================================================
FAQ.txt
History.txt
Manifest.txt
README.txt
Rakefile
lib/trollop.rb
test/test_trollop.rb
================================================
FILE: vendor/gems/trollop/README.txt
================================================
== trollop
by William Morgan (wmorgan-trollop at the masanjin dot nets or http://cs.stanford.edu/~ruby)
Main page: http://trollop.rubyforge.org
Release announcements and comments: http://all-thing.net/search/label/trollop
Documentation quickstart: See Trollop::options (for some reason rdoc isn't
linking that; it's in the top right of the screen if you're browsing online)
and then Trollop::Parser#opt. Also see the examples at http://trollop.rubyforge.org/.
== DESCRIPTION
Trollop is a commandline option parser for Ruby that just gets out of your
way. One line of code per option is all you need to write. For that, you get
a nice automatically-generated help page, robust option parsing, command
subcompletion, and sensible defaults for everything you don't specify.
== FEATURES/PROBLEMS
- Dirt-simple usage.
- Sensible defaults. No tweaking necessary, much tweaking possible.
- Support for long options, short options, short option bundling,
and automatic type validation and conversion.
- Support for subcommands.
- Automatic help message generation, wrapped to current screen width.
- Lots of unit tests.
== REQUIREMENTS
* A burning desire to write less code.
== INSTALL
* gem install trollop
== LICENSE
Copyright (c) 2008 William Morgan. Trollop is distributed under the same terms as Ruby.
================================================
FILE: vendor/gems/trollop/Rakefile
================================================
# -*- ruby -*-
require 'rubygems'
require 'hoe'
$:.unshift "lib"
require 'trollop'
class Hoe
def extra_dev_deps; @extra_dev_deps.reject { |x| x[0] == "hoe" } end
end
Hoe.new('trollop', Trollop::VERSION) do |p|
p.rubyforge_name = 'trollop'
p.author = "William Morgan"
p.summary = "Trollop is a commandline option parser for Ruby that just gets out of your way. One line of code per option is all you need to write. For that, you get a nice automatically-generated help page, robust option parsing, command subcompletion, and sensible defaults for everything you don't specify."
p.description = p.paragraphs_of('README.txt', 4..5, 9..18).join("\n\n").gsub(/== SYNOPSIS/, "Synopsis")
p.url = "http://trollop.rubyforge.org"
p.changes = p.paragraphs_of('History.txt', 0..0).join("\n\n")
p.email = "wmorgan-trollop@masanjin.net"
end
WWW_FILES = FileList["www/*"] + %w(README.txt FAQ.txt)
task :upload_webpage => WWW_FILES do |t|
sh "rsync -Paz -essh #{t.prerequisites * ' '} wmorgan@rubyforge.org:/var/www/gforge-projects/trollop/"
end
task :rdoc do |t|
sh "rdoc lib README.txt History.txt -m README.txt"
end
task :upload_docs => :rdoc do |t|
sh "rsync -az -essh doc/* wmorgan@rubyforge.org:/var/www/gforge-projects/trollop/trollop/"
end
# vim: syntax=ruby
================================================
FILE: vendor/gems/trollop/lib/trollop.rb
================================================
## lib/trollop.rb -- trollop command-line processing library
## Author:: William Morgan (mailto: wmorgan-trollop@masanjin.net)
## Copyright:: Copyright 2007 William Morgan
## License:: GNU GPL version 2
require 'date'
module Trollop
VERSION = "1.14"
## Thrown by Parser in the event of a commandline error. Not needed if
## you're using the Trollop::options entry.
class CommandlineError < StandardError; end
## Thrown by Parser if the user passes in '-h' or '--help'. Handled
## automatically by Trollop#options.
class HelpNeeded < StandardError; end
## Thrown by Parser if the user passes in '-h' or '--version'. Handled
## automatically by Trollop#options.
class VersionNeeded < StandardError; end
## Regex for floating point numbers
FLOAT_RE = /^-?((\d+(\.\d+)?)|(\.\d+))$/
## Regex for parameters
PARAM_RE = /^-(-|\.$|[^\d\.])/
## The commandline parser. In typical usage, the methods in this class
## will be handled internally by Trollop::options. In this case, only the
## #opt, #banner and #version, #depends, and #conflicts methods will
## typically be called.
##
## If it's necessary to instantiate this class (for more complicated
## argument-parsing situations), be sure to call #parse to actually
## produce the output hash.
class Parser
## The set of values that indicate a flag option when passed as the
## +:type+ parameter of #opt.
FLAG_TYPES = [:flag, :bool, :boolean]
## The set of values that indicate a single-parameter (normal) option when
## passed as the +:type+ parameter of #opt.
##
## A value of +io+ corresponds to a readable IO resource, including
## a filename, URI, or the strings 'stdin' or '-'.
SINGLE_ARG_TYPES = [:int, :integer, :string, :double, :float, :io, :date]
## The set of values that indicate a multiple-parameter option (i.e., that
## takes multiple space-separated values on the commandline) when passed as
## the +:type+ parameter of #opt.
MULTI_ARG_TYPES = [:ints, :integers, :strings, :doubles, :floats, :ios, :dates]
## The complete set of legal values for the +:type+ parameter of #opt.
TYPES = FLAG_TYPES + SINGLE_ARG_TYPES + MULTI_ARG_TYPES
INVALID_SHORT_ARG_REGEX = /[\d-]/ #:nodoc:
## The values from the commandline that were not interpreted by #parse.
attr_reader :leftovers
## The complete configuration hashes for each option. (Mainly useful
## for testing.)
attr_reader :specs
## Initializes the parser, and instance-evaluates any block given.
def initialize *a, &b
@version = nil
@leftovers = []
@specs = {}
@long = {}
@short = {}
@order = []
@constraints = []
@stop_words = []
@stop_on_unknown = false
#instance_eval(&b) if b # can't take arguments
cloaker(&b).bind(self).call(*a) if b
end
## Define an option. +name+ is the option name, a unique identifier
## for the option that you will use internally, which should be a
## symbol or a string. +desc+ is a string description which will be
## displayed in help messages.
##
## Takes the following optional arguments:
##
## [+:long+] Specify the long form of the argument, i.e. the form with two dashes. If unspecified, will be automatically derived based on the argument name by turning the +name+ option into a string, and replacing any _'s by -'s.
## [+:short+] Specify the short form of the argument, i.e. the form with one dash. If unspecified, will be automatically derived from +name+.
## [+:type+] Require that the argument take a parameter or parameters of type +type+. For a single parameter, the value can be a member of +SINGLE_ARG_TYPES+, or a corresponding Ruby class (e.g. +Integer+ for +:int+). For multiple-argument parameters, the value can be any member of +MULTI_ARG_TYPES+ constant. If unset, the default argument type is +:flag+, meaning that the argument does not take a parameter. The specification of +:type+ is not necessary if a +:default+ is given.
## [+:default+] Set the default value for an argument. Without a default value, the hash returned by #parse (and thus Trollop::options) will have a +nil+ value for this key unless the argument is given on the commandline. The argument type is derived automatically from the class of the default value given, so specifying a +:type+ is not necessary if a +:default+ is given. (But see below for an important caveat when +:multi+: is specified too.) If the argument is a flag, and the default is set to +true+, then if it is specified on the the commandline the value will be +false+.
## [+:required+] If set to +true+, the argument must be provided on the commandline.
## [+:multi+] If set to +true+, allows multiple occurrences of the option on the commandline. Otherwise, only a single instance of the option is allowed. (Note that this is different from taking multiple parameters. See below.)
##
## Note that there are two types of argument multiplicity: an argument
## can take multiple values, e.g. "--arg 1 2 3". An argument can also
## be allowed to occur multiple times, e.g. "--arg 1 --arg 2".
##
## Arguments that take multiple values should have a +:type+ parameter
## drawn from +MULTI_ARG_TYPES+ (e.g. +:strings+), or a +:default:+
## value of an array of the correct type (e.g. [String]). The
## value of this argument will be an array of the parameters on the
## commandline.
##
## Arguments that can occur multiple times should be marked with
## +:multi+ => +true+. The value of this argument will also be an array.
## In contrast with regular non-multi options, if not specified on
## the commandline, the default value will be [], not nil.
##
## These two attributes can be combined (e.g. +:type+ => +:strings+,
## +:multi+ => +true+), in which case the value of the argument will be
## an array of arrays.
##
## There's one ambiguous case to be aware of: when +:multi+: is true and a
## +:default+ is set to an array (of something), it's ambiguous whether this
## is a multi-value argument as well as a multi-occurrence argument.
## In thise case, Trollop assumes that it's not a multi-value argument.
## If you want a multi-value, multi-occurrence argument with a default
## value, you must specify +:type+ as well.
def opt name, desc="", opts={}
raise ArgumentError, "you already have an argument named '#{name}'" if @specs.member? name
## fill in :type
opts[:type] = # normalize
case opts[:type]
when :boolean, :bool; :flag
when :integer; :int
when :integers; :ints
when :double; :float
when :doubles; :floats
when Class
case opts[:type].name
when 'TrueClass', 'FalseClass'; :flag
when 'String'; :string
when 'Integer'; :int
when 'Float'; :float
when 'IO'; :io
when 'Date'; :date
else
raise ArgumentError, "unsupported argument type '#{opts[:type].class.name}'"
end
when nil; nil
else
raise ArgumentError, "unsupported argument type '#{opts[:type]}'" unless TYPES.include?(opts[:type])
opts[:type]
end
## for options with :multi => true, an array default doesn't imply
## a multi-valued argument. for that you have to specify a :type
## as well. (this is how we disambiguate an ambiguous situation;
## see the docs for Parser#opt for details.)
disambiguated_default =
if opts[:multi] && opts[:default].is_a?(Array) && !opts[:type]
opts[:default].first
else
opts[:default]
end
type_from_default =
case disambiguated_default
when Integer; :int
when Numeric; :float
when TrueClass, FalseClass; :flag
when String; :string
when IO; :io
when Date; :date
when Array
if opts[:default].empty?
raise ArgumentError, "multiple argument type cannot be deduced from an empty array for '#{opts[:default][0].class.name}'"
end
case opts[:default][0] # the first element determines the types
when Integer; :ints
when Numeric; :floats
when String; :strings
when IO; :ios
when Date; :dates
else
raise ArgumentError, "unsupported multiple argument type '#{opts[:default][0].class.name}'"
end
when nil; nil
else
raise ArgumentError, "unsupported argument type '#{opts[:default].class.name}'"
end
raise ArgumentError, ":type specification and default type don't match (default type is #{type_from_default})" if opts[:type] && type_from_default && opts[:type] != type_from_default
opts[:type] = opts[:type] || type_from_default || :flag
## fill in :long
opts[:long] = opts[:long] ? opts[:long].to_s : name.to_s.gsub("_", "-")
opts[:long] =
case opts[:long]
when /^--([^-].*)$/
$1
when /^[^-]/
opts[:long]
else
raise ArgumentError, "invalid long option name #{opts[:long].inspect}"
end
raise ArgumentError, "long option name #{opts[:long].inspect} is already taken; please specify a (different) :long" if @long[opts[:long]]
## fill in :short
opts[:short] = opts[:short].to_s if opts[:short] unless opts[:short] == :none
opts[:short] = case opts[:short]
when /^-(.)$/; $1
when nil, :none, /^.$/; opts[:short]
else raise ArgumentError, "invalid short option name '#{opts[:short].inspect}'"
end
if opts[:short]
raise ArgumentError, "short option name #{opts[:short].inspect} is already taken; please specify a (different) :short" if @short[opts[:short]]
raise ArgumentError, "a short option name can't be a number or a dash" if opts[:short] =~ INVALID_SHORT_ARG_REGEX
end
## fill in :default for flags
opts[:default] = false if opts[:type] == :flag && opts[:default].nil?
## autobox :default for :multi (multi-occurrence) arguments
opts[:default] = [opts[:default]] if opts[:default] && opts[:multi] && !opts[:default].is_a?(Array)
## fill in :multi
opts[:multi] ||= false
opts[:desc] ||= desc
@long[opts[:long]] = name
@short[opts[:short]] = name if opts[:short] && opts[:short] != :none
@specs[name] = opts
@order << [:opt, name]
end
## Sets the version string. If set, the user can request the version
## on the commandline. Should probably be of the form "
## ".
def version s=nil; @version = s if s; @version end
## Adds text to the help display. Can be interspersed with calls to
## #opt to build a multi-section help page.
def banner s; @order << [:text, s] end
alias :text :banner
## Marks two (or more!) options as requiring each other. Only handles
## undirected (i.e., mutual) dependencies. Directed dependencies are
## better modeled with Trollop::die.
def depends *syms
syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
@constraints << [:depends, syms]
end
## Marks two (or more!) options as conflicting.
def conflicts *syms
syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
@constraints << [:conflicts, syms]
end
## Defines a set of words which cause parsing to terminate when
## encountered, such that any options to the left of the word are
## parsed as usual, and options to the right of the word are left
## intact.
##
## A typical use case would be for subcommand support, where these
## would be set to the list of subcommands. A subsequent Trollop
## invocation would then be used to parse subcommand options, after
## shifting the subcommand off of ARGV.
def stop_on *words
@stop_words = [*words].flatten
end
## Similar to #stop_on, but stops on any unknown word when encountered
## (unless it is a parameter for an argument). This is useful for
## cases where you don't know the set of subcommands ahead of time,
## i.e., without first parsing the global options.
def stop_on_unknown
@stop_on_unknown = true
end
## Parses the commandline. Typically called by Trollop::options.
def parse cmdline=ARGV
vals = {}
required = {}
opt :version, "Print version and exit" if @version unless @specs[:version] || @long["version"]
opt :help, "Show this message" unless @specs[:help] || @long["help"]
@specs.each do |sym, opts|
required[sym] = true if opts[:required]
vals[sym] = opts[:default]
vals[sym] = [] if opts[:multi] && !opts[:default] # multi arguments default to [], not nil
end
resolve_default_short_options
## resolve symbols
given_args = {}
@leftovers = each_arg cmdline do |arg, params|
sym = case arg
when /^-([^-])$/
@short[$1]
when /^--([^-]\S*)$/
@long[$1]
else
raise CommandlineError, "invalid argument syntax: '#{arg}'"
end
raise CommandlineError, "unknown argument '#{arg}'" unless sym
if given_args.include?(sym) && !@specs[sym][:multi]
raise CommandlineError, "option '#{arg}' specified multiple times"
end
given_args[sym] ||= {}
given_args[sym][:arg] = arg
given_args[sym][:params] ||= []
# The block returns the number of parameters taken.
num_params_taken = 0
unless params.nil?
if SINGLE_ARG_TYPES.include?(@specs[sym][:type])
given_args[sym][:params] << params[0, 1] # take the first parameter
num_params_taken = 1
elsif MULTI_ARG_TYPES.include?(@specs[sym][:type])
given_args[sym][:params] << params # take all the parameters
num_params_taken = params.size
end
end
num_params_taken
end
## check for version and help args
raise VersionNeeded if given_args.include? :version
raise HelpNeeded if given_args.include? :help
## check constraint satisfaction
@constraints.each do |type, syms|
constraint_sym = syms.find { |sym| given_args[sym] }
next unless constraint_sym
case type
when :depends
syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym][:long]} requires --#{@specs[sym][:long]}" unless given_args.include? sym }
when :conflicts
syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym][:long]} conflicts with --#{@specs[sym][:long]}" if given_args.include?(sym) && (sym != constraint_sym) }
end
end
required.each do |sym, val|
raise CommandlineError, "option '#{sym}' must be specified" unless given_args.include? sym
end
## parse parameters
given_args.each do |sym, given_data|
arg = given_data[:arg]
params = given_data[:params]
opts = @specs[sym]
raise CommandlineError, "option '#{arg}' needs a parameter" if params.empty? && opts[:type] != :flag
vals["#{sym}_given".intern] = true # mark argument as specified on the commandline
case opts[:type]
when :flag
vals[sym] = !opts[:default]
when :int, :ints
vals[sym] = params.map { |pg| pg.map { |p| parse_integer_parameter p, arg } }
when :float, :floats
vals[sym] = params.map { |pg| pg.map { |p| parse_float_parameter p, arg } }
when :string, :strings
vals[sym] = params.map { |pg| pg.map { |p| p.to_s } }
when :io, :ios
vals[sym] = params.map { |pg| pg.map { |p| parse_io_parameter p, arg } }
when :date, :dates
vals[sym] = params.map { |pg| pg.map { |p| parse_date_parameter p, arg } }
end
if SINGLE_ARG_TYPES.include?(opts[:type])
unless opts[:multi] # single parameter
vals[sym] = vals[sym][0][0]
else # multiple options, each with a single parameter
vals[sym] = vals[sym].map { |p| p[0] }
end
elsif MULTI_ARG_TYPES.include?(opts[:type]) && !opts[:multi]
vals[sym] = vals[sym][0] # single option, with multiple parameters
end
# else: multiple options, with multiple parameters
end
## allow openstruct-style accessors
class << vals
def method_missing(m, *args)
self[m] || self[m.to_s]
end
end
vals
end
def parse_date_parameter param, arg #:nodoc:
begin
begin
time = Chronic.parse(param)
rescue NameError
# chronic is not available
end
time ? Date.new(time.year, time.month, time.day) : Date.parse(param)
rescue ArgumentError => e
raise CommandlineError, "option '#{arg}' needs a date"
end
end
## Print the help message to +stream+.
def educate stream=$stdout
width # just calculate it now; otherwise we have to be careful not to
# call this unless the cursor's at the beginning of a line.
left = {}
@specs.each do |name, spec|
left[name] = "--#{spec[:long]}" +
(spec[:short] && spec[:short] != :none ? ", -#{spec[:short]}" : "") +
case spec[:type]
when :flag; ""
when :int; " "
when :ints; " "
when :string; " "
when :strings; " "
when :float; " "
when :floats; " "
when :io; " "
when :ios; " "
when :date; " "
when :dates; " "
end
end
leftcol_width = left.values.map { |s| s.length }.max || 0
rightcol_start = leftcol_width + 6 # spaces
unless @order.size > 0 && @order.first.first == :text
stream.puts "#@version\n" if @version
stream.puts "Options:"
end
@order.each do |what, opt|
if what == :text
stream.puts wrap(opt)
next
end
spec = @specs[opt]
stream.printf " %#{leftcol_width}s: ", left[opt]
desc = spec[:desc] + begin
default_s = case spec[:default]
when $stdout; ""
when $stdin; ""
when $stderr; ""
when Array
spec[:default].join(", ")
else
spec[:default].to_s
end
if spec[:default]
if spec[:desc] =~ /\.$/
" (Default: #{default_s})"
else
" (default: #{default_s})"
end
else
""
end
end
stream.puts wrap(desc, :width => width - rightcol_start - 1, :prefix => rightcol_start)
end
end
def width #:nodoc:
@width ||= if $stdout.tty?
begin
require 'curses'
Curses::init_screen
x = Curses::cols
Curses::close_screen
x
rescue Exception
80
end
else
80
end
end
def wrap str, opts={} # :nodoc:
if str == ""
[""]
else
str.split("\n").map { |s| wrap_line s, opts }.flatten
end
end
private
## yield successive arg, parameter pairs
def each_arg args
remains = []
i = 0
until i >= args.length
if @stop_words.member? args[i]
remains += args[i .. -1]
return remains
end
case args[i]
when /^--$/ # arg terminator
remains += args[(i + 1) .. -1]
return remains
when /^--(\S+?)=(.*)$/ # long argument with equals
yield "--#{$1}", [$2]
i += 1
when /^--(\S+)$/ # long argument
params = collect_argument_parameters(args, i + 1)
unless params.empty?
num_params_taken = yield args[i], params
unless num_params_taken
if @stop_on_unknown
remains += args[i + 1 .. -1]
return remains
else
remains += params
end
end
i += 1 + num_params_taken
else # long argument no parameter
yield args[i], nil
i += 1
end
when /^-(\S+)$/ # one or more short arguments
shortargs = $1.split(//)
shortargs.each_with_index do |a, j|
if j == (shortargs.length - 1)
params = collect_argument_parameters(args, i + 1)
unless params.empty?
num_params_taken = yield "-#{a}", params
unless num_params_taken
if @stop_on_unknown
remains += args[i + 1 .. -1]
return remains
else
remains += params
end
end
i += 1 + num_params_taken
else # argument no parameter
yield "-#{a}", nil
i += 1
end
else
yield "-#{a}", nil
end
end
else
if @stop_on_unknown
remains += args[i .. -1]
return remains
else
remains << args[i]
i += 1
end
end
end
remains
end
def parse_integer_parameter param, arg
raise CommandlineError, "option '#{arg}' needs an integer" unless param =~ /^\d+$/
param.to_i
end
def parse_float_parameter param, arg
raise CommandlineError, "option '#{arg}' needs a floating-point number" unless param =~ FLOAT_RE
param.to_f
end
def parse_io_parameter param, arg
case param
when /^(stdin|-)$/i; $stdin
else
require 'open-uri'
begin
open param
rescue SystemCallError => e
raise CommandlineError, "file or url for option '#{arg}' cannot be opened: #{e.message}"
end
end
end
def collect_argument_parameters args, start_at
params = []
pos = start_at
while args[pos] && args[pos] !~ PARAM_RE && !@stop_words.member?(args[pos]) do
params << args[pos]
pos += 1
end
params
end
def resolve_default_short_options
@order.each do |type, name|
next unless type == :opt
opts = @specs[name]
next if opts[:short]
c = opts[:long].split(//).find { |d| d !~ INVALID_SHORT_ARG_REGEX && !@short.member?(d) }
raise ArgumentError, "can't generate a default short option name for #{opts[:long].inspect}: out of unique characters" unless c
opts[:short] = c
@short[c] = name
end
end
def wrap_line str, opts={}
prefix = opts[:prefix] || 0
width = opts[:width] || (self.width - 1)
start = 0
ret = []
until start > str.length
nextt =
if start + width >= str.length
str.length
else
x = str.rindex(/\s/, start + width)
x = str.index(/\s/, start) if x && x < start
x || str.length
end
ret << (ret.empty? ? "" : " " * prefix) + str[start ... nextt]
start = nextt + 1
end
ret
end
## instance_eval but with ability to handle block arguments
## thanks to why: http://redhanded.hobix.com/inspect/aBlockCostume.html
def cloaker &b
(class << self; self; end).class_eval do
define_method :cloaker_, &b
meth = instance_method :cloaker_
remove_method :cloaker_
meth
end
end
end
## The top-level entry method into Trollop. Creates a Parser object,
## passes the block to it, then parses +args+ with it, handling any
## errors or requests for help or version information appropriately (and
## then exiting). Modifies +args+ in place. Returns a hash of option
## values.
##
## The block passed in should contain zero or more calls to +opt+
## (Parser#opt), zero or more calls to +text+ (Parser#text), and
## probably a call to +version+ (Parser#version).
##
## The returned block contains a value for every option specified with
## +opt+. The value will be the value given on the commandline, or the
## default value if the option was not specified on the commandline. For
## every option specified on the commandline, a key "
To install, you have three options:
Trollop is a single file with no dependencies. You can just download it and throw it in your code directory.
You can use rubygems and command your computer to gem install trollop.
To hack, command your computer to git clone git://gitorious.org/trollop/mainline.git, or see the Trollop Gitorious page.
To understand, read the examples below, the FAQ, and the API docs.
Examples
Simple
require'trollop'opts=Trollop::optionsdoopt:monkey,"Use monkey mode"# a flag --monkey, defaulting to falseopt:goat,"Use goat mode",:default=>true# a flag --goat, defaulting to trueopt:num_limbs,"Number of limbs",:default=>4# an integer --num-limbs <i>, defaulting to 4opt:num_thumbs,"Number of thumbs",:type=>:int# an integer --num-thumbs <i>, defaulting to nilendpopts# a hash: { :monkey => false, :goat => true, :num_limbs => 4, :num_thumbs => nil }
Trollop::options returns a hash of values. That's all the output you get.
Underscores are converted to dashes. opt :hello_there corresponds to an option --hello-there.
All options are taken to be boolean flags, defaulting to false, unless you specify a default or a type. The type will be inferred from the default if given, so no need to specify both.
Short (one-character) option names are created automatically. You can set them manually with :short.
Medium
require'trollop'opts=Trollop::optionsdoversion"test 1.2.3 (c) 2008 William Morgan"banner<<-EOS
Test is an awesome program that does something very, very important.
Usage:
test [options] <filenames>+
where [options] are:
EOSopt:ignore,"Ignore incorrect values"opt:file,"Extra data filename to read in, with a very long option description like this one",:type=>Stringopt:volume,"Volume level",:default=>3.0opt:iters,"Number of iterations",:default=>5endTrollop::die:volume,"must be non-negative"ifopts[:volume]<0Trollop::die:file,"must exist"unlessFile.exist?(opts[:file])ifopts[:file]
You can specify a version and some banner text for the help message. --help and --version are defined for you.
Any special error-checking is done by you, on the option hash. You can call Trollop::die to die with a nice message.
Sub-commands, a la SVN and Git
require'trollop'## Here's a program called "magic". We want this behavior:#### magic delete <fn> => deletes a file## magic copy <fn> => copies a file#### So 'delete' and 'copy' are subcommands.#### There are some global options, which appear to the left of the subcommand.## There are some subcommand options, which appear to the right.#### Subcommand options can be specific to the subcommand. 'delete' might take## different options from 'copy'.#### We do this by calling Trollop twice; one for the global options and once for## the subcommand options. We need to tell Trollop what the subcommands are, so## that it stops processing the global options when it encounters one of them.SUB_COMMANDS=%w(delete copy)global_opts=Trollop::optionsdobanner"magic file deleting and copying utility"opt:dry_run,"Don't actually do anything",:short=>"-n"stop_onSUB_COMMANDSendcmd=ARGV.shift# get the subcommandcmd_opts=casecmdwhen"delete"# parse delete optionsTrollop::optionsdoopt:force,"Force deletion"endwhen"copy"# parse copy optionsTrollop::optionsdoopt:double,"Copy twice for safety's sake"endelseTrollop::die"unknown subcommand #{cmd.inspect}"endputs"Global options: #{global_opts.inspect}"puts"Subcommand: #{cmd.inspect}"puts"Subcommand options: #{cmd_opts.inspect}"puts"Remaining arguments: #{ARGV.inspect}"
================================================
FILE: vendor/gems/xml-simple/lib/xmlsimple.rb
================================================
# = XmlSimple
#
# Author:: Maik Schmidt
# Copyright:: Copyright (c) 2003-2009 Maik Schmidt
# License:: Distributes under the same terms as Ruby.
#
require 'rexml/document'
require 'stringio'
# Easy API to maintain XML (especially configuration files).
class XmlSimple
include REXML
@@VERSION = '1.0.12'
# A simple cache for XML documents that were already transformed
# by xml_in.
class Cache
# Creates and initializes a new Cache object.
def initialize
@mem_share_cache = {}
@mem_copy_cache = {}
end
# Saves a data structure into a file.
#
# data::
# Data structure to be saved.
# filename::
# Name of the file belonging to the data structure.
def save_storable(data, filename)
cache_file = get_cache_filename(filename)
File.open(cache_file, "w+") { |f| Marshal.dump(data, f) }
end
# Restores a data structure from a file. If restoring the data
# structure failed for any reason, nil will be returned.
#
# filename::
# Name of the file belonging to the data structure.
def restore_storable(filename)
cache_file = get_cache_filename(filename)
return nil unless File::exist?(cache_file)
return nil unless File::mtime(cache_file).to_i > File::mtime(filename).to_i
data = nil
File.open(cache_file) { |f| data = Marshal.load(f) }
data
end
# Saves a data structure in a shared memory cache.
#
# data::
# Data structure to be saved.
# filename::
# Name of the file belonging to the data structure.
def save_mem_share(data, filename)
@mem_share_cache[filename] = [Time::now.to_i, data]
end
# Restores a data structure from a shared memory cache. You
# should consider these elements as "read only". If restoring
# the data structure failed for any reason, nil will be
# returned.
#
# filename::
# Name of the file belonging to the data structure.
def restore_mem_share(filename)
get_from_memory_cache(filename, @mem_share_cache)
end
# Copies a data structure to a memory cache.
#
# data::
# Data structure to be copied.
# filename::
# Name of the file belonging to the data structure.
def save_mem_copy(data, filename)
@mem_share_cache[filename] = [Time::now.to_i, Marshal.dump(data)]
end
# Restores a data structure from a memory cache. If restoring
# the data structure failed for any reason, nil will be
# returned.
#
# filename::
# Name of the file belonging to the data structure.
def restore_mem_copy(filename)
data = get_from_memory_cache(filename, @mem_share_cache)
data = Marshal.load(data) unless data.nil?
data
end
private
# Returns the "cache filename" belonging to a filename, i.e.
# the extension '.xml' in the original filename will be replaced
# by '.stor'. If filename does not have this extension, '.stor'
# will be appended.
#
# filename::
# Filename to get "cache filename" for.
def get_cache_filename(filename)
filename.sub(/(\.xml)?$/, '.stor')
end
# Returns a cache entry from a memory cache belonging to a
# certain filename. If no entry could be found for any reason,
# nil will be returned.
#
# filename::
# Name of the file the cache entry belongs to.
# cache::
# Memory cache to get entry from.
def get_from_memory_cache(filename, cache)
return nil unless cache[filename]
return nil unless cache[filename][0] > File::mtime(filename).to_i
return cache[filename][1]
end
end
# Create a "global" cache.
@@cache = Cache.new
# Creates and intializes a new XmlSimple object.
#
# defaults::
# Default values for options.
def initialize(defaults = nil)
unless defaults.nil? || defaults.instance_of?(Hash)
raise ArgumentError, "Options have to be a Hash."
end
@default_options = normalize_option_names(defaults, (KNOWN_OPTIONS['in'] + KNOWN_OPTIONS['out']).uniq)
@options = Hash.new
@_var_values = nil
end
# Converts an XML document in the same way as the Perl module XML::Simple.
#
# string::
# XML source. Could be one of the following:
#
# - nil: Tries to load and parse '.xml'.
# - filename: Tries to load and parse filename.
# - IO object: Reads from object until EOF is detected and parses result.
# - XML string: Parses string.
#
# options::
# Options to be used.
def xml_in(string = nil, options = nil)
handle_options('in', options)
# If no XML string or filename was supplied look for scriptname.xml.
if string.nil?
string = File::basename($0).dup
string.sub!(/\.[^.]+$/, '')
string += '.xml'
directory = File::dirname($0)
@options['searchpath'].unshift(directory) unless directory.nil?
end
if string.instance_of?(String)
if string =~ /<.*?>/m
@doc = parse(string)
elsif string == '-'
@doc = parse($stdin.read)
else
filename = find_xml_file(string, @options['searchpath'])
if @options.has_key?('cache')
@options['cache'].each { |scheme|
case(scheme)
when 'storable'
content = @@cache.restore_storable(filename)
when 'mem_share'
content = @@cache.restore_mem_share(filename)
when 'mem_copy'
content = @@cache.restore_mem_copy(filename)
else
raise ArgumentError, "Unsupported caching scheme: <#{scheme}>."
end
return content if content
}
end
@doc = load_xml_file(filename)
end
elsif string.kind_of?(IO) || string.kind_of?(StringIO) || string.kind_of?(Zlib::GzipReader)
@doc = parse(string.read)
else
raise ArgumentError, "Could not parse object of type: <#{string.type}>."
end
result = collapse(@doc.root)
result = @options['keeproot'] ? merge({}, @doc.root.name, result) : result
put_into_cache(result, filename)
result
end
# This is the functional version of the instance method xml_in.
def XmlSimple.xml_in(string = nil, options = nil)
xml_simple = XmlSimple.new
xml_simple.xml_in(string, options)
end
# Converts a data structure into an XML document.
#
# ref::
# Reference to data structure to be converted into XML.
# options::
# Options to be used.
def xml_out(ref, options = nil)
handle_options('out', options)
if ref.instance_of?(Array)
ref = { @options['anonymoustag'] => ref }
end
if @options['keeproot']
keys = ref.keys
if keys.size == 1
ref = ref[keys[0]]
@options['rootname'] = keys[0]
end
elsif @options['rootname'] == ''
if ref.instance_of?(Hash)
refsave = ref
ref = {}
refsave.each { |key, value|
if !scalar(value)
ref[key] = value
else
ref[key] = [ value.to_s ]
end
}
end
end
@ancestors = []
xml = value_to_xml(ref, @options['rootname'], '')
@ancestors = nil
if @options['xmldeclaration']
xml = @options['xmldeclaration'] + "\n" + xml
end
if @options.has_key?('outputfile')
if @options['outputfile'].kind_of?(IO)
return @options['outputfile'].write(xml)
else
File.open(@options['outputfile'], "w") { |file| file.write(xml) }
end
end
xml
end
# This is the functional version of the instance method xml_out.
def XmlSimple.xml_out(hash, options = nil)
xml_simple = XmlSimple.new
xml_simple.xml_out(hash, options)
end
private
# Declare options that are valid for xml_in and xml_out.
KNOWN_OPTIONS = {
'in' => %w(
keyattr keeproot forcecontent contentkey noattr
searchpath forcearray suppressempty anonymoustag
cache grouptags normalisespace normalizespace
variables varattr keytosymbol attrprefix
),
'out' => %w(
keyattr keeproot contentkey noattr rootname
xmldeclaration outputfile noescape suppressempty
anonymoustag indent grouptags noindent attrprefix
)
}
# Define some reasonable defaults.
DEF_KEY_ATTRIBUTES = []
DEF_ROOT_NAME = 'opt'
DEF_CONTENT_KEY = 'content'
DEF_XML_DECLARATION = ""
DEF_ANONYMOUS_TAG = 'anon'
DEF_FORCE_ARRAY = true
DEF_INDENTATION = ' '
DEF_KEY_TO_SYMBOL = false
# Normalizes option names in a hash, i.e., turns all
# characters to lower case and removes all underscores.
# Additionally, this method checks, if an unknown option
# was used and raises an according exception.
#
# options::
# Hash to be normalized.
# known_options::
# List of known options.
def normalize_option_names(options, known_options)
return nil if options.nil?
result = Hash.new
options.each { |key, value|
lkey = key.downcase
lkey.gsub!(/_/, '')
if !known_options.member?(lkey)
raise ArgumentError, "Unrecognised option: #{lkey}."
end
result[lkey] = value
}
result
end
# Merges a set of options with the default options.
#
# direction::
# 'in': If options should be handled for xml_in.
# 'out': If options should be handled for xml_out.
# options::
# Options to be merged with the default options.
def handle_options(direction, options)
@options = options || Hash.new
raise ArgumentError, "Options must be a Hash!" unless @options.instance_of?(Hash)
unless KNOWN_OPTIONS.has_key?(direction)
raise ArgumentError, "Unknown direction: <#{direction}>."
end
known_options = KNOWN_OPTIONS[direction]
@options = normalize_option_names(@options, known_options)
unless @default_options.nil?
known_options.each { |option|
unless @options.has_key?(option)
if @default_options.has_key?(option)
@options[option] = @default_options[option]
end
end
}
end
unless @options.has_key?('noattr')
@options['noattr'] = false
end
if @options.has_key?('rootname')
@options['rootname'] = '' if @options['rootname'].nil?
else
@options['rootname'] = DEF_ROOT_NAME
end
if @options.has_key?('xmldeclaration') && @options['xmldeclaration'] == true
@options['xmldeclaration'] = DEF_XML_DECLARATION
end
@options['keytosymbol'] = DEF_KEY_TO_SYMBOL unless @options.has_key?('keytosymbol')
if @options.has_key?('contentkey')
if @options['contentkey'] =~ /^-(.*)$/
@options['contentkey'] = $1
@options['collapseagain'] = true
end
else
@options['contentkey'] = DEF_CONTENT_KEY
end
unless @options.has_key?('normalisespace')
@options['normalisespace'] = @options['normalizespace']
end
@options['normalisespace'] = 0 if @options['normalisespace'].nil?
if @options.has_key?('searchpath')
unless @options['searchpath'].instance_of?(Array)
@options['searchpath'] = [ @options['searchpath'] ]
end
else
@options['searchpath'] = []
end
if @options.has_key?('cache') && scalar(@options['cache'])
@options['cache'] = [ @options['cache'] ]
end
@options['anonymoustag'] = DEF_ANONYMOUS_TAG unless @options.has_key?('anonymoustag')
if !@options.has_key?('indent') || @options['indent'].nil?
@options['indent'] = DEF_INDENTATION
end
@options['indent'] = '' if @options.has_key?('noindent')
# Special cleanup for 'keyattr' which could be an array or
# a hash or left to default to array.
if @options.has_key?('keyattr')
if !scalar(@options['keyattr'])
# Convert keyattr => { elem => '+attr' }
# to keyattr => { elem => ['attr', '+'] }
if @options['keyattr'].instance_of?(Hash)
@options['keyattr'].each { |key, value|
if value =~ /^([-+])?(.*)$/
@options['keyattr'][key] = [$2, $1 ? $1 : '']
end
}
elsif !@options['keyattr'].instance_of?(Array)
raise ArgumentError, "'keyattr' must be String, Hash, or Array!"
end
else
@options['keyattr'] = [ @options['keyattr'] ]
end
else
@options['keyattr'] = DEF_KEY_ATTRIBUTES
end
if @options.has_key?('forcearray')
if @options['forcearray'].instance_of?(Regexp)
@options['forcearray'] = [ @options['forcearray'] ]
end
if @options['forcearray'].instance_of?(Array)
force_list = @options['forcearray']
unless force_list.empty?
@options['forcearray'] = {}
force_list.each { |tag|
if tag.instance_of?(Regexp)
unless @options['forcearray']['_regex'].instance_of?(Array)
@options['forcearray']['_regex'] = []
end
@options['forcearray']['_regex'] << tag
else
@options['forcearray'][tag] = true
end
}
else
@options['forcearray'] = false
end
else
@options['forcearray'] = @options['forcearray'] ? true : false
end
else
@options['forcearray'] = DEF_FORCE_ARRAY
end
if @options.has_key?('grouptags') && !@options['grouptags'].instance_of?(Hash)
raise ArgumentError, "Illegal value for 'GroupTags' option - expected a Hash."
end
if @options.has_key?('variables') && !@options['variables'].instance_of?(Hash)
raise ArgumentError, "Illegal value for 'Variables' option - expected a Hash."
end
if @options.has_key?('variables')
@_var_values = @options['variables']
elsif @options.has_key?('varattr')
@_var_values = {}
end
end
# Actually converts an XML document element into a data structure.
#
# element::
# The document element to be collapsed.
def collapse(element)
result = @options['noattr'] ? {} : get_attributes(element)
if @options['normalisespace'] == 2
result.each { |k, v| result[k] = normalise_space(v) }
end
if element.has_elements?
element.each_element { |child|
value = collapse(child)
if empty(value) && (element.attributes.empty? || @options['noattr'])
next if @options.has_key?('suppressempty') && @options['suppressempty'] == true
end
result = merge(result, child.name, value)
}
if has_mixed_content?(element)
# normalisespace?
content = element.texts.map { |x| x.to_s }
content = content[0] if content.size == 1
result[@options['contentkey']] = content
end
elsif element.has_text? # i.e. it has only text.
return collapse_text_node(result, element)
end
# Turn Arrays into Hashes if key fields present.
count = fold_arrays(result)
# Disintermediate grouped tags.
if @options.has_key?('grouptags')
result.each { |key, value|
next unless (value.instance_of?(Hash) && (value.size == 1))
child_key, child_value = value.to_a[0]
if @options['grouptags'][key] == child_key
result[key] = child_value
end
}
end
# Fold Hashes containing a single anonymous Array up into just the Array.
if count == 1
anonymoustag = @options['anonymoustag']
if result.has_key?(anonymoustag) && result[anonymoustag].instance_of?(Array)
return result[anonymoustag]
end
end
if result.empty? && @options.has_key?('suppressempty')
return @options['suppressempty'] == '' ? '' : nil
end
result
end
# Collapses a text node and merges it with an existing Hash, if
# possible.
# Thanks to Curtis Schofield for reporting a subtle bug.
#
# hash::
# Hash to merge text node value with, if possible.
# element::
# Text node to be collapsed.
def collapse_text_node(hash, element)
value = node_to_text(element)
if empty(value) && !element.has_attributes?
return {}
end
if element.has_attributes? && !@options['noattr']
return merge(hash, @options['contentkey'], value)
else
if @options['forcecontent']
return merge(hash, @options['contentkey'], value)
else
return value
end
end
end
# Folds all arrays in a Hash.
#
# hash::
# Hash to be folded.
def fold_arrays(hash)
fold_amount = 0
keyattr = @options['keyattr']
if (keyattr.instance_of?(Array) || keyattr.instance_of?(Hash))
hash.each { |key, value|
if value.instance_of?(Array)
if keyattr.instance_of?(Array)
hash[key] = fold_array(value)
else
hash[key] = fold_array_by_name(key, value)
end
fold_amount += 1
end
}
end
fold_amount
end
# Folds an Array to a Hash, if possible. Folding happens
# according to the content of keyattr, which has to be
# an array.
#
# array::
# Array to be folded.
def fold_array(array)
hash = Hash.new
array.each { |x|
return array unless x.instance_of?(Hash)
key_matched = false
@options['keyattr'].each { |key|
if x.has_key?(key)
key_matched = true
value = x[key]
return array if value.instance_of?(Hash) || value.instance_of?(Array)
value = normalise_space(value) if @options['normalisespace'] == 1
x.delete(key)
hash[value] = x
break
end
}
return array unless key_matched
}
hash = collapse_content(hash) if @options['collapseagain']
hash
end
# Folds an Array to a Hash, if possible. Folding happens
# according to the content of keyattr, which has to be
# a Hash.
#
# name::
# Name of the attribute to be folded upon.
# array::
# Array to be folded.
def fold_array_by_name(name, array)
return array unless @options['keyattr'].has_key?(name)
key, flag = @options['keyattr'][name]
hash = Hash.new
array.each { |x|
if x.instance_of?(Hash) && x.has_key?(key)
value = x[key]
return array if value.instance_of?(Hash) || value.instance_of?(Array)
value = normalise_space(value) if @options['normalisespace'] == 1
hash[value] = x
hash[value]["-#{key}"] = hash[value][key] if flag == '-'
hash[value].delete(key) unless flag == '+'
else
$stderr.puts("Warning: <#{name}> element has no '#{key}' attribute.")
return array
end
}
hash = collapse_content(hash) if @options['collapseagain']
hash
end
# Tries to collapse a Hash even more ;-)
#
# hash::
# Hash to be collapsed again.
def collapse_content(hash)
content_key = @options['contentkey']
hash.each_value { |value|
return hash unless value.instance_of?(Hash) && value.size == 1 && value.has_key?(content_key)
hash.each_key { |key| hash[key] = hash[key][content_key] }
}
hash
end
# Adds a new key/value pair to an existing Hash. If the key to be added
# does already exist and the existing value associated with key is not
# an Array, it will be converted into an Array. Then the new value is
# appended to that Array.
#
# hash::
# Hash to add key/value pair to.
# key::
# Key to be added.
# value::
# Value to be associated with key.
def merge(hash, key, value)
if value.instance_of?(String)
value = normalise_space(value) if @options['normalisespace'] == 2
# do variable substitutions
unless @_var_values.nil? || @_var_values.empty?
value.gsub!(/\$\{(\w+)\}/) { |x| get_var($1) }
end
# look for variable definitions
if @options.has_key?('varattr')
varattr = @options['varattr']
if hash.has_key?(varattr)
set_var(hash[varattr], value)
end
end
end
#patch for converting keys to symbols
if @options.has_key?('keytosymbol')
if @options['keytosymbol'] == true
key = key.to_s.downcase.to_sym
end
end
if hash.has_key?(key)
if hash[key].instance_of?(Array)
hash[key] << value
else
hash[key] = [ hash[key], value ]
end
elsif value.instance_of?(Array) # Handle anonymous arrays.
hash[key] = [ value ]
else
if force_array?(key)
hash[key] = [ value ]
else
hash[key] = value
end
end
hash
end
# Checks, if the 'forcearray' option has to be used for
# a certain key.
def force_array?(key)
return false if key == @options['contentkey']
return true if @options['forcearray'] == true
forcearray = @options['forcearray']
if forcearray.instance_of?(Hash)
return true if forcearray.has_key?(key)
return false unless forcearray.has_key?('_regex')
forcearray['_regex'].each { |x| return true if key =~ x }
end
return false
end
# Converts the attributes array of a document node into a Hash.
# Returns an empty Hash, if node has no attributes.
#
# node::
# Document node to extract attributes from.
def get_attributes(node)
attributes = {}
if @options['attrprefix']
node.attributes.each { |n,v| attributes["@" + n] = v }
else
node.attributes.each { |n,v| attributes[n] = v }
end
attributes
end
# Determines, if a document element has mixed content.
#
# element::
# Document element to be checked.
def has_mixed_content?(element)
if element.has_text? && element.has_elements?
return true if element.texts.join('') !~ /^\s*$/s
end
false
end
# Called when a variable definition is encountered in the XML.
# A variable definition looks like
# value
# where attrname matches the varattr setting.
def set_var(name, value)
@_var_values[name] = value
end
# Called during variable substitution to get the value for the
# named variable.
def get_var(name)
if @_var_values.has_key?(name)
return @_var_values[name]
else
return "${#{name}}"
end
end
# Recurses through a data structure building up and returning an
# XML representation of that structure as a string.
#
# ref::
# Reference to the data structure to be encoded.
# name::
# The XML tag name to be used for this item.
# indent::
# A string of spaces for use as the current indent level.
def value_to_xml(ref, name, indent)
named = !name.nil? && name != ''
nl = @options.has_key?('noindent') ? '' : "\n"
if !scalar(ref)
if @ancestors.member?(ref)
raise ArgumentError, "Circular data structures not supported!"
end
@ancestors << ref
else
if named
return [indent, '<', name, '>', @options['noescape'] ? ref.to_s : escape_value(ref.to_s), '', name, '>', nl].join('')
else
return ref.to_s + nl
end
end
# Unfold hash to array if possible.
if ref.instance_of?(Hash) && !ref.empty? && !@options['keyattr'].empty? && indent != ''
ref = hash_to_array(name, ref)
end
result = []
if ref.instance_of?(Hash)
# Reintermediate grouped values if applicable.
if @options.has_key?('grouptags')
ref.each { |key, value|
if @options['grouptags'].has_key?(key)
ref[key] = { @options['grouptags'][key] => value }
end
}
end
nested = []
text_content = nil
if named
result << indent << '<' << name
end
if !ref.empty?
ref.each { |key, value|
next if !key.nil? && key[0, 1] == '-'
if value.nil?
unless @options.has_key?('suppressempty') && @options['suppressempty'].nil?
raise ArgumentError, "Use of uninitialized value!"
end
value = {}
end
# Check for the '@' attribute prefix to allow separation of attributes and elements
if @options['noattr'] ||
(@options['attrprefix'] && !(key =~ /^@(.*)/)) ||
!scalar(value)
nested << value_to_xml(value, key, indent + @options['indent'])
else
value = value.to_s
value = escape_value(value) unless @options['noescape']
if key == @options['contentkey']
text_content = value
else
result << ' ' << ($1||key) << '="' << value << '"'
end
end
}
else
text_content = ''
end
if !nested.empty? || !text_content.nil?
if named
result << '>'
if !text_content.nil?
result << text_content
nested[0].sub!(/^\s+/, '') if !nested.empty?
else
result << nl
end
if !nested.empty?
result << nested << indent
end
result << '' << name << '>' << nl
else
result << nested
end
else
result << ' />' << nl
end
elsif ref.instance_of?(Array)
ref.each { |value|
if scalar(value)
result << indent << '<' << name << '>'
result << (@options['noescape'] ? value.to_s : escape_value(value.to_s))
result << '' << name << '>' << nl
elsif value.instance_of?(Hash)
result << value_to_xml(value, name, indent)
else
result << indent << '<' << name << '>' << nl
result << value_to_xml(value, @options['anonymoustag'], indent + @options['indent'])
result << indent << '' << name << '>' << nl
end
}
else
# Probably, this is obsolete.
raise ArgumentError, "Can't encode a value of type: #{ref.type}."
end
@ancestors.pop if !scalar(ref)
result.join('')
end
# Checks, if a certain value is a "scalar" value. Whatever
# that will be in Ruby ... ;-)
#
# value::
# Value to be checked.
def scalar(value)
return false if value.instance_of?(Hash) || value.instance_of?(Array)
return true
end
# Attempts to unfold a hash of hashes into an array of hashes. Returns
# a reference to th array on success or the original hash, if unfolding
# is not possible.
#
# parent::
#
# hashref::
# Reference to the hash to be unfolded.
def hash_to_array(parent, hashref)
arrayref = []
hashref.each { |key, value|
return hashref unless value.instance_of?(Hash)
if @options['keyattr'].instance_of?(Hash)
return hashref unless @options['keyattr'].has_key?(parent)
arrayref << { @options['keyattr'][parent][0] => key }.update(value)
else
arrayref << { @options['keyattr'][0] => key }.update(value)
end
}
arrayref
end
# Replaces XML markup characters by their external entities.
#
# data::
# The string to be escaped.
def escape_value(data)
Text::normalize(data)
end
# Removes leading and trailing whitespace and sequences of
# whitespaces from a string.
#
# text::
# String to be normalised.
def normalise_space(text)
text.strip.gsub(/\s\s+/, ' ')
end
# Checks, if an object is nil, an empty String or an empty Hash.
# Thanks to Norbert Gawor for a bugfix.
#
# value::
# Value to be checked for emptyness.
def empty(value)
case value
when Hash
return value.empty?
when String
return value !~ /\S/m
else
return value.nil?
end
end
# Converts a document node into a String.
# If the node could not be converted into a String
# for any reason, default will be returned.
#
# node::
# Document node to be converted.
# default::
# Value to be returned, if node could not be converted.
def node_to_text(node, default = nil)
if node.instance_of?(REXML::Element)
node.texts.map { |t| t.value }.join('')
elsif node.instance_of?(REXML::Attribute)
node.value.nil? ? default : node.value.strip
elsif node.instance_of?(REXML::Text)
node.value.strip
else
default
end
end
# Parses an XML string and returns the according document.
#
# xml_string::
# XML string to be parsed.
#
# The following exception may be raised:
#
# REXML::ParseException::
# If the specified file is not wellformed.
def parse(xml_string)
Document.new(xml_string)
end
# Searches in a list of paths for a certain file. Returns
# the full path to the file, if it could be found. Otherwise,
# an exception will be raised.
#
# filename::
# Name of the file to search for.
# searchpath::
# List of paths to search in.
def find_xml_file(file, searchpath)
filename = File::basename(file)
if filename != file
return file if File::file?(file)
else
searchpath.each { |path|
full_path = File::join(path, filename)
return full_path if File::file?(full_path)
}
end
if searchpath.empty?
return file if File::file?(file)
raise ArgumentError, "File does not exist: #{file}."
end
raise ArgumentError, "Could not find <#{filename}> in <#{searchpath.join(':')}>"
end
# Loads and parses an XML configuration file.
#
# filename::
# Name of the configuration file to be loaded.
#
# The following exceptions may be raised:
#
# Errno::ENOENT::
# If the specified file does not exist.
# REXML::ParseException::
# If the specified file is not wellformed.
def load_xml_file(filename)
parse(IO::read(filename))
end
# Caches the data belonging to a certain file.
#
# data::
# Data to be cached.
# filename::
# Name of file the data was read from.
def put_into_cache(data, filename)
if @options.has_key?('cache')
@options['cache'].each { |scheme|
case(scheme)
when 'storable'
@@cache.save_storable(data, filename)
when 'mem_share'
@@cache.save_mem_share(data, filename)
when 'mem_copy'
@@cache.save_mem_copy(data, filename)
else
raise ArgumentError, "Unsupported caching scheme: <#{scheme}>."
end
}
end
end
end
# vim:sw=2