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-a70a936e0913 r-c72545ae 481524450359 poolparty-fake_cloud i-7fd89416 ami-bf5eb9d6 16 running ip-10-250-46-144.ec2.internal ec2-75-101-141-103.compute-1.amazonaws.com test_key 0 m1.small 2009-05-31T17:56:42.000Z us-east-1c aki-a71cf9ce ari-a51cf9cc r-e9b4ff80 481524450359 poolparty-fake_cloud i-7f000516 ami-0d729464 16 running ip-10-251-74-198.ec2.internal ec2-67-202-10-73.compute-1.amazonaws.com test_key 0 m1.small 2009-07-20T20:35:51.000Z us-east-1c aki-a71cf9ce ari-a51cf9cc ================================================ FILE: test/fixtures/ec2/ec2-describe-security-groups_response_body.xml ================================================ 1234567890 poolparty-fake_cloud poolparty-fake_cloud tcp 80 80 0.0.0.0/0 ================================================ FILE: test/fixtures/ec2/ec2-run-instances_response_body.xml ================================================ c1128a8d-59e6-4aad-8923-12b07ff873ec r-51D70925 admin admin-default i-485C07DE emi-39CA160F 0 pending 0.0.0.0 0.0.0.0 eucalyptus_sample 0 m1.small 2009-07-22T00:09:29.293Z garden eki-AE9D17DD eri-171A1927 false ================================================ FILE: test/fixtures/ec2/ec2-terminate-instances_response_body.xml ================================================ d16d104e-f450-4d5c-8354-70a8cad2dacd i-3B3506A0 32 shutting-down 16 running ================================================ FILE: test/fixtures/ec2/elb-describe-load-balancers.xml ================================================ hemant-watson-lb hemant-watson-lb-1501915372.us-east-1.elb.amazonaws.com HTTP 80 8080 us-east-1c i-61835409 i-ab8453c3 TCP:8080 30 5 2 10 2009-09-22T22:00:35.790Z minerva-pool minerva-pool-1740816084.us-east-1.elb.amazonaws.com HTTP 80 80 us-east-1a i-49b67f21 i-a3935dcb i-ad26efc5 i-f531f89d TCP:80 30 5 2 10 2009-09-18T01:42:43.470Z 61cc2880-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 "