├── .gitignore ├── .gitmodules ├── .rspec ├── .ruby-version ├── CONTRIBUTING.md ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── NOTICE ├── Procfile ├── README.md ├── Rakefile ├── Vagrantfile ├── bin ├── dea ├── run_specs_in_vm.sh └── start_warden_and_run_specs.sh ├── buildpacks ├── README.md ├── bin │ └── run └── lib │ ├── buildpack.rb │ ├── errors.rb │ ├── git.rb │ ├── installer.rb │ └── procfile.rb ├── config └── dea.yml ├── go └── src │ ├── common │ ├── config.go │ └── logger.go │ ├── directoryserver │ ├── deaclient.go │ ├── deaclient_test.go │ ├── directoryserver.go │ ├── directoryserver_test.go │ ├── helper_test.go │ ├── log.go │ ├── log_test.go │ ├── max_latency_writer.go │ ├── max_latency_writer_test.go │ ├── stream_handler.go │ └── stream_handler_test.go │ └── runner │ └── runner.go ├── lib ├── container │ ├── container.rb │ └── warden_client_provider.rb ├── dea.rb ├── dea │ ├── bootstrap.rb │ ├── config.rb │ ├── directory_server │ │ ├── directory_server_v2.rb │ │ ├── hmac_helper.rb │ │ ├── instance_paths.rb │ │ └── staging_tasks.rb │ ├── droplet.rb │ ├── droplet_registry.rb │ ├── env.rb │ ├── env │ │ ├── exporter.rb │ │ └── strategy_chooser.rb │ ├── health_check │ │ ├── base.rb │ │ ├── port_open.rb │ │ └── state_file_ready.rb │ ├── http │ │ ├── app_paths.rb │ │ └── httpserver.rb │ ├── lifecycle │ │ ├── evacuation_handler.rb │ │ ├── shutdown_handler.rb │ │ └── signal_handler.rb │ ├── loggregator.rb │ ├── nats.rb │ ├── pid_file.rb │ ├── promise.rb │ ├── protocol.rb │ ├── registry_enumeration.rb │ ├── resource_manager.rb │ ├── responders │ │ ├── dea_locator.rb │ │ ├── http_staging.rb │ │ ├── nats_staging.rb │ │ └── staging.rb │ ├── router_client.rb │ ├── snapshot.rb │ ├── staging │ │ ├── admin_buildpack_downloader.rb │ │ ├── buildpack_manager.rb │ │ ├── buildpacks_message.rb │ │ ├── env.rb │ │ ├── staging_message.rb │ │ ├── staging_task.rb │ │ ├── staging_task_registry.rb │ │ └── staging_task_workspace.rb │ ├── starting │ │ ├── database_uri_generator.rb │ │ ├── env.rb │ │ ├── instance.rb │ │ ├── instance_manager.rb │ │ ├── instance_registry.rb │ │ ├── instance_uri_updater.rb │ │ ├── start_message.rb │ │ └── startup_script_generator.rb │ ├── stat_collector.rb │ ├── task.rb │ ├── user_facing_errors.rb │ ├── utils.rb │ ├── utils │ │ ├── cloud_controller_client.rb │ │ ├── download.rb │ │ ├── egress_rules_mapper.rb │ │ ├── event_emitter.rb │ │ ├── eventmachine_multipart_hack.rb │ │ ├── hm9000.rb │ │ ├── non_blocking_unzipper.rb │ │ ├── sync_upload.rb │ │ ├── upload.rb │ │ └── uri_cleaner.rb │ └── version.rb └── tasks │ └── dir_server.rb ├── pre_integration.sh └── spec ├── bin └── file_server.rb ├── fixtures ├── apps │ ├── java_with_oome │ │ ├── META-INF │ │ │ ├── MANIFEST.MF │ │ │ └── maven │ │ │ │ └── org.cloudfoundry.test │ │ │ │ └── simple-spring-app │ │ │ │ ├── pom.properties │ │ │ │ └── pom.xml │ │ └── WEB-INF │ │ │ ├── classes │ │ │ ├── com │ │ │ │ └── springdeveloper │ │ │ │ │ └── test │ │ │ │ │ ├── CrashBean.class │ │ │ │ │ ├── HomeController.class │ │ │ │ │ └── MessageBean.class │ │ │ ├── empty.properties │ │ │ ├── log4j.xml │ │ │ ├── system.properties │ │ │ └── test.properties │ │ │ ├── lib │ │ │ ├── aopalliance-1.0.jar │ │ │ ├── aspectjrt-1.6.9.jar │ │ │ ├── javax.inject-1.jar │ │ │ ├── jcl-over-slf4j-1.5.10.jar │ │ │ ├── jstl-1.2.jar │ │ │ ├── log4j-1.2.15.jar │ │ │ ├── slf4j-api-1.5.10.jar │ │ │ ├── slf4j-log4j12-1.5.10.jar │ │ │ ├── spring-aop-3.1.0.RELEASE.jar │ │ │ ├── spring-asm-3.1.0.RELEASE.jar │ │ │ ├── spring-beans-3.1.0.RELEASE.jar │ │ │ ├── spring-context-3.1.0.RELEASE.jar │ │ │ ├── spring-context-support-3.1.0.RELEASE.jar │ │ │ ├── spring-core-3.1.0.RELEASE.jar │ │ │ ├── spring-expression-3.1.0.RELEASE.jar │ │ │ ├── spring-web-3.1.0.RELEASE.jar │ │ │ └── spring-webmvc-3.1.0.RELEASE.jar │ │ │ ├── spring │ │ │ ├── appServlet │ │ │ │ └── servlet-context.xml │ │ │ └── root-context.xml │ │ │ ├── views │ │ │ └── home.jsp │ │ │ └── web.xml │ ├── node_with_invalid_procfile │ │ ├── Procfile │ │ ├── app.js │ │ └── package.json │ ├── node_with_procfile │ │ ├── Procfile │ │ ├── app.js │ │ ├── cloudfoundry.json │ │ ├── npm-shrinkwrap.json │ │ └── package.json │ ├── node_without_procfile │ │ ├── app.js │ │ └── package.json │ ├── rails3_with_db │ │ ├── .gitignore │ │ ├── Gemfile │ │ ├── Gemfile.lock │ │ ├── README │ │ ├── Rakefile │ │ ├── app │ │ │ ├── controllers │ │ │ │ ├── application_controller.rb │ │ │ │ └── service_controller.rb │ │ │ ├── helpers │ │ │ │ ├── application_helper.rb │ │ │ │ └── root_helper.rb │ │ │ ├── models │ │ │ │ ├── data_value.rb │ │ │ │ └── mongo_data_value.rb │ │ │ └── views │ │ │ │ ├── layouts │ │ │ │ └── application.html.erb │ │ │ │ └── root │ │ │ │ └── index.html.erb │ │ ├── config.ru │ │ ├── config │ │ │ ├── application.rb │ │ │ ├── boot.rb │ │ │ ├── database.yml │ │ │ ├── environment.rb │ │ │ ├── environments │ │ │ │ ├── development.rb │ │ │ │ ├── production.rb │ │ │ │ └── test.rb │ │ │ ├── initializers │ │ │ │ ├── backtrace_silencers.rb │ │ │ │ ├── inflections.rb │ │ │ │ ├── mime_types.rb │ │ │ │ ├── mongo.rb │ │ │ │ ├── redis.rb │ │ │ │ ├── secret_token.rb │ │ │ │ └── session_store.rb │ │ │ ├── locales │ │ │ │ └── en.yml │ │ │ └── routes.rb │ │ ├── db │ │ │ ├── migrate │ │ │ │ └── 20110513200739_create_data_values.rb │ │ │ ├── schema.rb │ │ │ └── seeds.rb │ │ ├── lib │ │ │ └── tasks │ │ │ │ └── .gitkeep │ │ ├── manifest.yml │ │ ├── public │ │ │ ├── 404.html │ │ │ ├── 422.html │ │ │ ├── 500.html │ │ │ ├── favicon.ico │ │ │ ├── images │ │ │ │ └── rails.png │ │ │ ├── javascripts │ │ │ │ ├── .gitkeep │ │ │ │ └── application.js │ │ │ ├── robots.txt │ │ │ └── stylesheets │ │ │ │ └── .gitkeep │ │ ├── script │ │ │ └── rails │ │ ├── test │ │ │ ├── functional │ │ │ │ └── root_controller_test.rb │ │ │ ├── performance │ │ │ │ └── browsing_test.rb │ │ │ ├── test_helper.rb │ │ │ └── unit │ │ │ │ ├── helpers │ │ │ │ └── root_helper_test.rb │ │ │ │ └── widget_test.rb │ │ └── vendor │ │ │ └── cache │ │ │ ├── abstract-1.0.0.gem │ │ │ ├── actionmailer-3.0.6.gem │ │ │ ├── actionpack-3.0.6.gem │ │ │ ├── activemodel-3.0.6.gem │ │ │ ├── activerecord-3.0.6.gem │ │ │ ├── activeresource-3.0.6.gem │ │ │ ├── activesupport-3.0.6.gem │ │ │ ├── arel-2.0.9.gem │ │ │ ├── bson-1.2.4.gem │ │ │ ├── builder-2.1.2.gem │ │ │ ├── carrot-0.8.1.gem │ │ │ ├── daemons-1.1.2.gem │ │ │ ├── erubis-2.6.6.gem │ │ │ ├── eventmachine-0.12.10.gem │ │ │ ├── i18n-0.5.0.gem │ │ │ ├── json-1.5.1.gem │ │ │ ├── mail-2.2.15.gem │ │ │ ├── mime-types-1.16.gem │ │ │ ├── mongo-1.2.4.gem │ │ │ ├── mongo_mapper-0.9.0.gem │ │ │ ├── mysql2-0.3.11.gem │ │ │ ├── pg-0.11.0.gem │ │ │ ├── plucky-0.3.7.gem │ │ │ ├── polyglot-0.3.1.gem │ │ │ ├── rack-1.2.2.gem │ │ │ ├── rack-mount-0.6.14.gem │ │ │ ├── rack-test-0.5.7.gem │ │ │ ├── rails-3.0.6.gem │ │ │ ├── railties-3.0.6.gem │ │ │ ├── rake-0.8.7.gem │ │ │ ├── redis-2.2.0.gem │ │ │ ├── thin-1.2.11.gem │ │ │ ├── thor-0.14.6.gem │ │ │ ├── treetop-1.4.9.gem │ │ │ └── tzinfo-0.3.26.gem │ └── sinatra │ │ ├── .profile.d │ │ └── set_envs.sh │ │ ├── Gemfile │ │ ├── Gemfile.lock │ │ └── app.rb ├── buildpack.zip ├── buildpack_cache │ └── cached_file ├── certs │ ├── ca.crt │ ├── client.crt │ ├── client.key │ ├── hm9000_ca.crt │ ├── hm9000_client.crt │ ├── hm9000_client.key │ ├── hm9000_server.crt │ ├── hm9000_server.key │ ├── server.crt │ └── server.key ├── fake_buildpacks │ ├── 5_second_compiling_buildpack │ │ └── bin │ │ │ ├── compile │ │ │ ├── detect │ │ │ └── release │ ├── admin_buildpack │ │ └── bin │ │ │ ├── compile │ │ │ ├── detect │ │ │ └── release │ ├── fail_to_detect │ │ └── bin │ │ │ └── detect │ ├── graceful_shutdown │ │ └── bin │ │ │ ├── compile │ │ │ ├── detect │ │ │ ├── release │ │ │ └── run.sh │ ├── long_compiling_buildpack │ │ └── bin │ │ │ ├── compile │ │ │ ├── detect │ │ │ └── release │ ├── no_start_command │ │ └── bin │ │ │ ├── compile │ │ │ ├── detect │ │ │ └── release │ ├── ruby │ │ └── bin │ │ │ ├── compile │ │ │ ├── detect │ │ │ └── release │ └── start_command │ │ └── bin │ │ ├── compile │ │ ├── detect │ │ └── release └── hooks │ ├── after_start │ ├── after_stop │ ├── before_start │ └── before_stop ├── integration ├── config.yml ├── directory_server_spec.rb ├── evacuation_spec.rb ├── running_app_spec.rb ├── staging_running_spec.rb └── staging_spec.rb ├── spec_helper.rb ├── support ├── bootstrap_setup.rb ├── custom_matchers.rb ├── em_helpers.rb ├── factory.rb ├── fake_connection.rb ├── fake_emitter.rb ├── integration_helpers │ ├── buildpack_helpers.rb │ ├── dea_helpers.rb │ ├── nats_helper.rb │ ├── process_helpers.rb │ └── staging_helpers.rb ├── mock_class.rb ├── nats.rb ├── promise.rb ├── registry_shared_examples.rb ├── staging_spec_helpers.rb └── tmpdir.rb └── unit ├── bootstrap ├── directed_start_spec.rb ├── discovery_spec.rb ├── droplet_information_spec.rb ├── handle_dea_stop_spec.rb ├── heartbeat_spec.rb ├── router_registration_spec.rb └── snapshot_spec.rb ├── bootstrap_spec.rb ├── buildpack ├── buildpack_spec.rb ├── git_spec.rb ├── installer_spec.rb └── procfile_spec.rb ├── config_spec.rb ├── container ├── container_spec.rb └── warden_client_provider_spec.rb ├── directory_server ├── directory_server_v2_spec.rb ├── hmac_helper_spec.rb ├── instance_paths_spec.rb └── staging_tasks_spec.rb ├── droplet_registry_spec.rb ├── droplet_spec.rb ├── env ├── exporter_spec.rb └── strategy_chooser_spec.rb ├── env_spec.rb ├── health_check ├── port_open_spec.rb └── state_file_ready_spec.rb ├── http ├── app_paths_spec.rb └── httpserver_spec.rb ├── lifecycle ├── evacuation_handler_spec.rb ├── shutdown_handler_spec.rb └── signal_handler_spec.rb ├── loggregator_spec.rb ├── nats_spec.rb ├── pid_file_spec.rb ├── promise_spec.rb ├── resource_manager_spec.rb ├── responders ├── dea_locator_spec.rb ├── http_staging_spec.rb ├── nats_staging_spec.rb └── staging_spec.rb ├── router_client_spec.rb ├── staging ├── admin_buildpack_downloader_spec.rb ├── buildpack_manager_spec.rb ├── buildpacks_message_spec.rb ├── env_spec.rb ├── staging_message_spec.rb ├── staging_task_registry_spec.rb ├── staging_task_spec.rb └── staging_task_workspace_spec.rb ├── starting ├── database_uri_generator_spec.rb ├── env_spec.rb ├── instance_manager_spec.rb ├── instance_registry_spec.rb ├── instance_spec.rb ├── instance_uri_updater_spec.rb ├── start_message_spec.rb └── startup_script_generator_spec.rb ├── stat_collector_spec.rb ├── task_spec.rb ├── user_facing_errors_spec.rb ├── utils ├── cloud_controller_client_spec.rb ├── download_spec.rb ├── egress_rules_mapper_spec.rb ├── eventmachine_multipart_hack_spec.rb ├── hm9000_spec.rb ├── non_blocking_unzipper_spec.rb ├── upload_spec.rb └── uri_cleaner_spec.rb └── utils_spec.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle 2 | .idea 3 | .vagrant* 4 | /go/ 5 | coverage 6 | spec/fixtures/**/.git 7 | tmp/* 8 | !.gitkeep 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "go/vendor"] 2 | path = go/vendor 3 | url = https://github.com/cloudfoundry/govendor 4 | [submodule "go/src/github.com/cloudfoundry/gosteno"] 5 | path = go/src/github.com/cloudfoundry/gosteno 6 | url = https://github.com/cloudfoundry/gosteno.git 7 | [submodule "go/src/github.com/go-yaml/yaml"] 8 | path = go/src/github.com/go-yaml/yaml 9 | url = https://github.com/go-yaml/yaml 10 | [submodule "go/src/github.com/go-check/check"] 11 | path = go/src/github.com/go-check/check 12 | url = https://github.com/go-check/check 13 | [submodule "github.com/go-fsnotify/fsnotify"] 14 | path = go/src/github.com/go-fsnotify/fsnotify 15 | url = https://github.com/go-fsnotify/fsnotify 16 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --backtrace 2 | --color 3 | --order rand 4 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.3.1 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to dea_ng 2 | 3 | The Cloud Foundry team uses GitHub and accepts contributions via 4 | [pull request](https://help.github.com/articles/using-pull-requests). 5 | 6 | ## Contributor License Agreement 7 | 8 | Follow these steps to make a contribution to any of our open source repositories: 9 | 10 | 1. Ensure that you have completed our CLA Agreement for 11 | [individuals](http://www.cloudfoundry.org/individualcontribution.pdf) or 12 | [corporations](http://www.cloudfoundry.org/corpcontribution.pdf). 13 | 14 | 1. Set your name and email (these should match the information on your submitted CLA) 15 | 16 | git config --global user.name "Firstname Lastname" 17 | git config --global user.email "your_email@example.com" 18 | 19 | ## General Workflow 20 | 21 | 1. Fork the repository 22 | 1. Create a feature branch (`git checkout -b better_dea`) 23 | 1. Make changes on your branch 24 | 1. [Run dea tests](https://github.com/cloudfoundry/dea_ng#running-the-dea-in-the-provided-vagrant-vm) 25 | 1. Push to your fork (`git push origin better_dea`) and submit a pull request 26 | 27 | We favor pull requests with very small, single commits with a single purpose. 28 | 29 | Your pull request is much more likely to be accepted if: 30 | 31 | * Your pull request includes tests 32 | 33 | * Your pull request is small and focused with a clear message that conveys the intent of your change 34 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'eventmachine' 4 | gem 'em-http-request' 5 | gem 'em-synchrony' 6 | 7 | gem 'em-warden-client', git: 'https://github.com/cloudfoundry/warden.git' 8 | gem 'warden-client', git: 'https://github.com/cloudfoundry/warden.git' 9 | gem 'warden-protocol', git: 'https://github.com/cloudfoundry/warden.git' 10 | 11 | gem 'nats', require: 'nats/client', git: 'https://github.com/nats-io/ruby-nats.git' 12 | gem 'rack', require: %w[rack/utils rack/mime] 13 | gem 'rake' 14 | gem 'thin' 15 | gem 'yajl-ruby', require: %w[yajl yajl/json_gem] 16 | gem 'grape', git: 'https://github.com/intridea/grape.git' 17 | gem 'httpclient' 18 | 19 | gem 'steno', '~> 1.1.0' 20 | 21 | gem 'membrane' 22 | gem 'posix-spawn', '~> 0.3.6' 23 | gem 'uuidtools' 24 | 25 | gem 'vmstat' 26 | 27 | gem 'loggregator_emitter', git: 'https://github.com/cloudfoundry/loggregator_emitter.git' 28 | 29 | gem 'sys-filesystem' 30 | 31 | group :test do 32 | gem 'codeclimate-test-reporter', require: false 33 | gem 'ci_reporter_rspec' 34 | gem 'foreman' 35 | gem 'net-ssh' 36 | gem 'patron' 37 | gem 'rack-test' 38 | gem 'rspec' 39 | gem 'rspec-eventually' 40 | gem 'rubyzip' 41 | gem 'sinatra' 42 | gem 'timecop' 43 | gem 'webmock' 44 | gem 'pry' 45 | end 46 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | cf-dea_ng 2 | Copyright (c) 2013-Present CloudFoundry.org Foundation, Inc. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | directory_server: rake dir_server:run 2 | nats: go/bin/gnatsd 3 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "ci/reporter/rake/rspec" 4 | require "rspec/core/rake_task" 5 | require "rspec/core/version" 6 | Dir.glob(File.expand_path("../lib/tasks/*", __FILE__)).each { |f| require f } 7 | 8 | reports_dir = File.expand_path("spec_reports") 9 | 10 | ENV["CI_REPORTS"] = reports_dir 11 | 12 | task :ensure_coding do 13 | patterns = [ 14 | /Rakefile$/, 15 | /\.rb$/, 16 | ] 17 | 18 | files = `git ls-files`.split.select do |file| 19 | patterns.any? { |e| e.match(file) } 20 | end 21 | 22 | header = "# coding: UTF-8\n\n" 23 | 24 | files.each do |file| 25 | content = File.read(file) 26 | 27 | unless content.start_with?(header) 28 | File.open(file, "w") do |f| 29 | f.write(header) 30 | f.write(content) 31 | end 32 | end 33 | end 34 | end 35 | 36 | namespace :config do 37 | desc "Check if configuration file is valid" 38 | task :check, :file do |t, args| 39 | require "yaml" 40 | require "dea/config" 41 | 42 | config = YAML.load(File.read(args[:file])) 43 | Dea::Config.schema.validate(config) 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | 3 | config.vm.box = "cloudfoundry/warden-compatible" 4 | 5 | config.vm.synced_folder '~/workspace/cf-release', '/var/cf-release' 6 | 7 | # Requires vagrant-aws and unf plugins 8 | config.vm.provider :aws do |aws, override| 9 | override.vm.box = "dummy" 10 | override.vm.box_url = "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box" 11 | 12 | override.ssh.private_key_path = ENV["WARDEN_AWS_PRIVATE_KEY_PATH"] 13 | 14 | aws.ami = ENV["WARDEN_COMPATIBLE_AMI"] || "ami-e0b64188" 15 | aws.access_key_id = ENV["AWS_ACCESS_KEY_ID"] 16 | aws.secret_access_key = ENV["AWS_SECRET_ACCESS_KEY"] 17 | aws.instance_type = "m3.medium" 18 | aws.tags = { "Name" => "dea-test" } 19 | aws.block_device_mapping = [{ 20 | :DeviceName => "/dev/sda1", 'Ebs.VolumeSize' => 40 21 | }] 22 | end 23 | 24 | # Required for gem dependencies 25 | config.vm.provision "shell", inline: "sudo apt-get update" 26 | config.vm.provision "shell", inline: "sudo apt-get -q -y install libcurl4-gnutls-dev" 27 | end 28 | -------------------------------------------------------------------------------- /bin/dea: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | 4 | require 'rubygems' 5 | require 'bundler/setup' 6 | 7 | $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__)) 8 | 9 | require "eventmachine" 10 | require "yaml" 11 | 12 | require "dea/bootstrap" 13 | 14 | unless ARGV.size == 1 15 | abort "Usage: dea " 16 | end 17 | 18 | begin 19 | config = YAML.load_file(ARGV[0]) 20 | rescue => e 21 | abort "ERROR: Failed loading config: #{e}" 22 | end 23 | 24 | bootstrap = Dea::Bootstrap.new(config) 25 | 26 | EM.epoll 27 | 28 | begin 29 | EM.run do 30 | bootstrap.setup 31 | bootstrap.start 32 | end 33 | rescue => e 34 | STDERR.puts "Encountered error: #{e}\n#{e.backtrace.join("\n")}" 35 | end 36 | -------------------------------------------------------------------------------- /bin/run_specs_in_vm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e -x -u 3 | 4 | cd $( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/.. 5 | vagrant up 6 | echo "===== VAGRANT BOX PROVISIONED AND STARTED =====" 7 | 8 | 9 | echo "about to ssh to run tests" 10 | date 11 | 12 | if [ -z ${NOTEST:=} ]; then 13 | vagrant ssh -c "/var/cf-release/src/dea-hm-workspace/src/dea_next/bin/start_warden_and_run_specs.sh" 14 | fi 15 | -------------------------------------------------------------------------------- /bin/start_warden_and_run_specs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e -x -u 3 | 4 | # Run directory server tests 5 | 6 | export GOPATH=/var/cf-release/src/dea-hm-workspace/src/dea_next/go 7 | go test -i -race directoryserver 8 | go test -v -race directoryserver 9 | 10 | # Start warden 11 | 12 | mkdir -p /tmp/warden/rootfs 13 | 14 | echo -n "Extracting rootfs..." 15 | sudo tar -xf /var/cf-release/.blobs/`basename $(readlink /var/cf-release/blobs/rootfs/*)` -C /tmp/warden/rootfs 16 | echo "finished" 17 | 18 | ( 19 | cd /var/cf-release/src/dea-hm-workspace/src/warden/warden 20 | sudo bundle install 21 | bundle exec rake setup:bin 22 | sudo bundle exec rake warden:start[config/linux.yml] &> /tmp/warden.log & 23 | ) 24 | 25 | echo "waiting for warden to come up" 26 | while [ ! -e /tmp/warden.sock ] 27 | do 28 | sleep 1 29 | done 30 | echo "warden is ready" 31 | 32 | # Start foreman (directory server & nats) 33 | 34 | cd /var/cf-release/src/dea-hm-workspace/src/dea_next 35 | go get github.com/nats-io/gnatsd 36 | sudo bundle install 37 | sudo bundle exec foreman start &> /tmp/foreman.log & 38 | 39 | # Run specs 40 | 41 | exit_code=0 42 | bundle install 43 | bundle exec rspec spec/unit -fd 44 | exit_code=$? 45 | 46 | bundle exec rspec spec/integration -fd 47 | exit_code=$? 48 | 49 | echo "Tests finished: killing background jobs:" 50 | jobs 51 | 52 | JOBS="ruby rake runner" 53 | for j in $JOBS 54 | do 55 | sudo killall $j 56 | done 57 | 58 | exit $exit_code 59 | -------------------------------------------------------------------------------- /buildpacks/README.md: -------------------------------------------------------------------------------- 1 | === Staging Overview 2 | 3 | === Directory Structure 4 | 5 | === Implementing a Staging Plugin in Ruby 6 | 7 | === Implementing a Staging Plugin in Something Else 8 | -------------------------------------------------------------------------------- /buildpacks/bin/run: -------------------------------------------------------------------------------- 1 | $:.unshift(File.expand_path("../../lib", __FILE__)) 2 | 3 | require "buildpack" 4 | 5 | unless ARGV.length == 1 6 | puts "Usage: run_plugin [plugin config file]" 7 | exit 1 8 | end 9 | 10 | Buildpacks::Buildpack.from_file(ARGV[0]).stage_application -------------------------------------------------------------------------------- /buildpacks/lib/errors.rb: -------------------------------------------------------------------------------- 1 | module Buildpacks 2 | class StagingError < StandardError; end 3 | 4 | class NoAppDetectedError < StagingError; end 5 | class BuildpackCompileFailed < StagingError; end 6 | class BuildpackReleaseFailed < StagingError; end 7 | end 8 | -------------------------------------------------------------------------------- /buildpacks/lib/git.rb: -------------------------------------------------------------------------------- 1 | require "uri" 2 | 3 | module Buildpacks 4 | class Git 5 | def self.parse(repository) 6 | parsed = URI(repository) 7 | branch = parsed.fragment if parsed.fragment 8 | parsed.fragment = nil 9 | return parsed, branch 10 | end 11 | 12 | def self.clone(repository, destination) 13 | git_url, branch = parse(repository) 14 | target_dir = File.join(destination, File.basename(git_url.path, File.extname(git_url.path))) 15 | 16 | git_branch_option = branch.to_s.empty? ? "" : "-b #{branch}" 17 | cmd = "git clone --depth 1 #{git_branch_option} --recursive #{git_url} #{target_dir}" 18 | ok = system(*cmd.split) 19 | 20 | if !ok 21 | cmd = "git clone --recursive #{git_url} #{target_dir}" 22 | ok = system(*cmd.split) 23 | raise "Git clone failed: #{cmd}" unless ok 24 | checkout(branch, target_dir) if branch 25 | end 26 | 27 | target_dir 28 | end 29 | 30 | def self.checkout(branch, git_dir) 31 | cmd = "git --git-dir=#{git_dir}/.git --work-tree=#{git_dir} checkout #{branch}" 32 | ok = system(*cmd.split) 33 | raise "Git checkout failed: #{cmd}" unless ok 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /buildpacks/lib/installer.rb: -------------------------------------------------------------------------------- 1 | require "open3" 2 | require "errors" 3 | 4 | module Buildpacks 5 | class Installer < Struct.new(:path, :app_dir, :cache_dir) 6 | def detect 7 | @detect_output, status = Open3.capture2 command('detect') 8 | status == 0 9 | rescue => e 10 | puts "Failed to run buildpack detection script with error: #{e}" 11 | false 12 | end 13 | 14 | def name 15 | @detect_output ? @detect_output.strip : nil 16 | end 17 | 18 | def compile 19 | ok = system "#{command('compile')} #{cache_dir}" 20 | raise BuildpackCompileFailed, "Buildpack compilation step failed" unless ok 21 | end 22 | 23 | def release_info 24 | output, status = Open3.capture2 command("release") 25 | raise BuildpackReleaseFailed, "Release info failed:\n#{output}" unless status == 0 26 | YAML.load(output) 27 | end 28 | 29 | private 30 | 31 | def command(command_name) 32 | "#{path}/bin/#{command_name} #{app_dir}" 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /buildpacks/lib/procfile.rb: -------------------------------------------------------------------------------- 1 | module Buildpacks 2 | class Procfile 3 | def initialize(path) 4 | @path = path 5 | end 6 | 7 | def contents 8 | @contents ||= begin 9 | if File.exists?(@path) 10 | contents = YAML.load(File.read(@path)) 11 | raise(ArgumentError, "Invalid Procfile format. Please ensure it is a valid YAML hash") unless contents.is_a? Hash 12 | contents 13 | end 14 | end 15 | end 16 | 17 | def web 18 | contents["web"] if contents 19 | end 20 | end 21 | end -------------------------------------------------------------------------------- /config/dea.yml: -------------------------------------------------------------------------------- 1 | # See src/lib/dea/config.rb for optional config values. 2 | 3 | # Base directory for dea, application directories, dea temp files, etc. are all relative to this. 4 | base_dir: /tmp/dea_ng 5 | 6 | ssl: 7 | port: 8443 8 | key_file: spec/fixtures/certs/server.key 9 | cert_file: spec/fixtures/certs/server.crt 10 | 11 | logging: 12 | level: debug 13 | 14 | loggregator: 15 | router: 127.0.0.1:3456 16 | 17 | resources: 18 | memory_mb: 2048 19 | memory_overcommit_factor: 2 20 | disk_mb: 2048 21 | disk_overcommit_factor: 2 22 | 23 | nats_servers: 24 | - nats://127.0.0.1:4222/ 25 | 26 | pid_filename: /tmp/dea_ng.pid 27 | 28 | warden_socket: /tmp/warden.sock 29 | 30 | evacuation_bail_out_time_in_seconds: 900 31 | 32 | default_health_check_timeout: 60 # 1 minute 33 | 34 | index: 0 35 | domain: "domain.example.com" 36 | 37 | staging: 38 | enabled: true 39 | environment: 40 | PATH: /usr/local/ruby/bin 41 | BUILDPACK_CACHE: /var/vcap/packages/buildpack_cache 42 | memory_limit_mb: 1024 43 | disk_limit_mb: 2048 44 | disk_inode_limit: 200000 45 | cpu_limit_shares: 512 46 | max_staging_duration: 900 # 15 minutes 47 | 48 | instance: 49 | disk_inode_limit: 200000 50 | memory_to_cpu_share_ratio: 8 51 | max_cpu_share_limit: 256 52 | min_cpu_share_limit: 1 53 | nproc_limit: 1024 54 | 55 | dea_ruby: /usr/bin/ruby 56 | 57 | # For Go-based directory server 58 | directory_server: 59 | protocol: 'http' 60 | v2_port: 80 61 | file_api_port: 1234 62 | streaming_timeout: 10 63 | logging: 64 | level: info 65 | 66 | stacks: 67 | - name: cflinuxfs2 68 | package_path: /tmp/warden/rootfs 69 | # Hook scripts for droplet start/stop 70 | # hooks: 71 | # before_start: path/to/script 72 | # after_start: path/to/script 73 | # before_stop: path/to/script 74 | # after_stop: path/to/script 75 | 76 | placement_properties: 77 | zone: "CRAZY_TOWN" 78 | 79 | cc_url: https://user:password@cloud_controller_ng.service.cf.internal 80 | 81 | hm9000: 82 | listener_uri: https://hm9000.service.cf.internal:5335 83 | key_file: spec/fixtures/certs/hm9000_client.key 84 | cert_file: spec/fixtures/certs/hm9000_client.crt 85 | ca_file: spec/fixtures/certs/hm9000_ca.crt 86 | -------------------------------------------------------------------------------- /go/src/common/config.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "io/ioutil" 5 | goyaml "github.com/go-yaml/yaml" 6 | ) 7 | 8 | type Config struct { 9 | Server DirServerConfig "directory_server" 10 | } 11 | 12 | type DirServerConfig struct { 13 | DeaPort uint16 "file_api_port" 14 | DirServerPort uint16 "v2_port" 15 | StreamingTimeout uint32 "streaming_timeout" 16 | Logging LogConfig "logging" 17 | } 18 | 19 | type LogConfig struct { 20 | Level string "level" 21 | File string "file" 22 | Syslog string "syslog" 23 | } 24 | 25 | func ConfigFromFile(configPath string) (*Config, error) { 26 | configBytes, err := ioutil.ReadFile(configPath) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | config := Config{} 32 | if err := goyaml.Unmarshal(configBytes, &config); err != nil { 33 | return nil, err 34 | } 35 | 36 | return &config, nil 37 | } 38 | -------------------------------------------------------------------------------- /go/src/common/logger.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | steno "github.com/cloudfoundry/gosteno" 5 | "os" 6 | ) 7 | 8 | func SetupSteno(logConfig *LogConfig) { 9 | level, err := steno.GetLogLevel(logConfig.Level) 10 | if err != nil { 11 | panic(err) 12 | } 13 | 14 | sinks := make([]steno.Sink, 0) 15 | if logConfig.File != "" { 16 | sinks = append(sinks, steno.NewFileSink(logConfig.File)) 17 | } else { 18 | sinks = append(sinks, steno.NewIOSink(os.Stdout)) 19 | } 20 | if logConfig.Syslog != "" { 21 | sinks = append(sinks, steno.NewSyslogSink(logConfig.Syslog)) 22 | } 23 | 24 | stenoConfig := &steno.Config{ 25 | Sinks: sinks, 26 | Codec: steno.NewJsonCodec(), 27 | Level: level, 28 | } 29 | 30 | steno.Init(stenoConfig) 31 | } 32 | -------------------------------------------------------------------------------- /go/src/directoryserver/deaclient.go: -------------------------------------------------------------------------------- 1 | package directoryserver 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | ) 10 | 11 | type DeaClient struct { 12 | Host string 13 | Port uint16 14 | } 15 | 16 | var ( 17 | ErrUnreachable = errors.New("DEA is unreachable") 18 | ErrNotOK = errors.New("DEA did not respond with 200 status") 19 | ErrInvalidJson = errors.New("DEA responded with invalid JSON") 20 | ) 21 | 22 | // LookupPath makes a request to a DEA on behalf of the specified request. 23 | // On errors, it writes the error to the specified response writer and returns 24 | // one of the defined errors to the caller. 25 | // On success, it returns the path extracted from the DEA response. 26 | func (x *DeaClient) LookupPath(w http.ResponseWriter, r *http.Request) (string, error) { 27 | y := fmt.Sprintf("http://%s:%d%s", x.Host, x.Port, r.URL.String()) 28 | 29 | log.Infof("Sending HTTP request to DEA: %s", y) 30 | 31 | res, err := http.Get(y) 32 | if err != nil { 33 | http.Error(w, ErrUnreachable.Error(), http.StatusInternalServerError) 34 | return "", ErrUnreachable 35 | } 36 | 37 | defer res.Body.Close() 38 | 39 | if res.StatusCode != 200 { 40 | // Forward DEA response 41 | for h, i := range res.Header { 42 | w.Header().Del(h) 43 | for _, j := range i { 44 | w.Header().Add(h, j) 45 | } 46 | } 47 | 48 | w.WriteHeader(res.StatusCode) 49 | io.Copy(w, res.Body) 50 | return "", ErrNotOK 51 | } 52 | 53 | d := json.NewDecoder(res.Body) 54 | m := make(map[string]interface{}) 55 | err = d.Decode(&m) 56 | if err != nil { 57 | http.Error(w, ErrInvalidJson.Error(), http.StatusInternalServerError) 58 | return "", ErrInvalidJson 59 | } 60 | 61 | p, ok := m["instance_path"].(string) 62 | if !ok { 63 | http.Error(w, ErrInvalidJson.Error(), http.StatusInternalServerError) 64 | return "", ErrInvalidJson 65 | } 66 | 67 | return p, nil 68 | } 69 | -------------------------------------------------------------------------------- /go/src/directoryserver/helper_test.go: -------------------------------------------------------------------------------- 1 | package directoryserver 2 | 3 | import ( 4 | "io/ioutil" 5 | "net" 6 | "net/http" 7 | "strconv" 8 | "strings" 9 | "testing" 10 | "github.com/go-check/check" 11 | ) 12 | 13 | func Test(t *testing.T) { check.TestingT(t) } 14 | 15 | func getBody(response *http.Response) ([]byte, error) { 16 | defer response.Body.Close() 17 | return ioutil.ReadAll(response.Body) 18 | } 19 | 20 | func checkRequest(received *http.Request, expected *http.Request) bool { 21 | badMethod := received.Method != expected.Method 22 | badUrl := !strings.HasSuffix(expected.URL.String(), 23 | received.URL.String()) 24 | badProto := received.Proto != expected.Proto 25 | badHost := received.Host != expected.Host 26 | 27 | if badMethod || badUrl || badProto || badHost { 28 | return false 29 | } 30 | 31 | return true 32 | } 33 | 34 | type dummyDeaHandler struct { 35 | t *check.C 36 | expRequest *http.Request 37 | responseBody *[]byte 38 | } 39 | 40 | func (handler dummyDeaHandler) ServeHTTP(w http.ResponseWriter, 41 | r *http.Request) { 42 | if !checkRequest(r, handler.expRequest) { 43 | handler.t.Fail() 44 | } 45 | 46 | w.Header()["Content-Length"] = []string{strconv. 47 | Itoa(len(*(handler.responseBody)))} 48 | w.Write(*(handler.responseBody)) 49 | } 50 | 51 | func startTestServer(h http.Handler) (net.Listener, string, uint16) { 52 | l, err := net.Listen("tcp", "localhost:0") 53 | if err != nil { 54 | panic(err) 55 | } 56 | 57 | x := http.Server{Handler: h} 58 | go x.Serve(l) 59 | 60 | hs, ps, err := net.SplitHostPort(l.Addr().String()) 61 | if err != nil { 62 | panic(err) 63 | } 64 | 65 | hx := hs 66 | px, _ := strconv.Atoi(ps) 67 | 68 | return l, hx, uint16(px) 69 | } 70 | -------------------------------------------------------------------------------- /go/src/directoryserver/log.go: -------------------------------------------------------------------------------- 1 | package directoryserver 2 | 3 | import ( 4 | steno "github.com/cloudfoundry/gosteno" 5 | ) 6 | 7 | var log *steno.Logger 8 | 9 | func init() { 10 | initializeLogger() 11 | } 12 | 13 | func initializeLogger() { 14 | log = steno.NewLogger("directory_server") 15 | } 16 | -------------------------------------------------------------------------------- /go/src/directoryserver/log_test.go: -------------------------------------------------------------------------------- 1 | package directoryserver 2 | 3 | import ( 4 | steno "github.com/cloudfoundry/gosteno" 5 | "os" 6 | ) 7 | 8 | func init() { 9 | steno.Init(&steno.Config{ 10 | Sinks: []steno.Sink{steno.NewIOSink(os.Stderr)}, 11 | Codec: steno.NewJsonCodec(), 12 | Level: steno.LOG_ALL, 13 | }) 14 | 15 | log = steno.NewLogger("directory_server_test") 16 | } 17 | -------------------------------------------------------------------------------- /go/src/directoryserver/max_latency_writer_test.go: -------------------------------------------------------------------------------- 1 | package directoryserver 2 | 3 | import ( 4 | "github.com/go-check/check" 5 | "time" 6 | ) 7 | 8 | type testWriteFlusher struct { 9 | WriteCounter int 10 | FlushCounter int 11 | } 12 | 13 | func (x *testWriteFlusher) Write(data []byte) (int, error) { 14 | x.WriteCounter += len(data) 15 | return len(data), nil 16 | } 17 | 18 | func (x *testWriteFlusher) Flush() { 19 | x.FlushCounter++ 20 | } 21 | 22 | type MaxLatencyWriterSuite struct{} 23 | 24 | var _ = check.Suite(&MaxLatencyWriterSuite{}) 25 | 26 | func (s *MaxLatencyWriterSuite) TestWrite(c *check.C) { 27 | x := &testWriteFlusher{} 28 | y := NewMaxLatencyWriter(x, 10*time.Millisecond) 29 | 30 | c.Check(x.WriteCounter, check.Equals, 0) 31 | 32 | y.Write([]byte("x")) 33 | 34 | c.Check(x.WriteCounter, check.Equals, 1) 35 | 36 | y.Stop() 37 | } 38 | 39 | func (s *MaxLatencyWriterSuite) TestFlush(c *check.C) { 40 | x := &testWriteFlusher{} 41 | y := NewMaxLatencyWriter(x, 10*time.Millisecond) 42 | 43 | y.writeLock.Lock() 44 | c.Check(x.FlushCounter, check.Equals, 0) 45 | y.writeLock.Unlock() 46 | 47 | time.Sleep(15 * time.Millisecond) 48 | 49 | y.writeLock.Lock() 50 | c.Check(x.FlushCounter, check.Equals, 1) 51 | y.writeLock.Unlock() 52 | 53 | y.Stop() 54 | } 55 | 56 | func (s *MaxLatencyWriterSuite) TestStop(c *check.C) { 57 | x := &testWriteFlusher{} 58 | y := NewMaxLatencyWriter(x, 10*time.Millisecond) 59 | 60 | c.Check(x.FlushCounter, check.Equals, 0) 61 | 62 | y.Stop() 63 | 64 | time.Sleep(15 * time.Millisecond) 65 | 66 | c.Check(x.FlushCounter, check.Equals, 0) 67 | } 68 | 69 | func (s *MaxLatencyWriterSuite) TestDoubleStop(c *check.C) { 70 | x := &testWriteFlusher{} 71 | y := NewMaxLatencyWriter(x, 10*time.Millisecond) 72 | 73 | c.Check(x.FlushCounter, check.Equals, 0) 74 | 75 | y.Stop() 76 | y.Stop() 77 | 78 | time.Sleep(15 * time.Millisecond) 79 | 80 | c.Check(x.FlushCounter, check.Equals, 0) 81 | } 82 | -------------------------------------------------------------------------------- /go/src/runner/runner.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | Starts the HTTP-based directory server and listens for connections. 5 | Reads configuration from the co-located DEA's YAML configuration file. 6 | 7 | Usage: 8 | $> runner 9 | */ 10 | import ( 11 | "common" 12 | "directoryserver" 13 | "flag" 14 | steno "github.com/cloudfoundry/gosteno" 15 | "net" 16 | "strings" 17 | ) 18 | 19 | // Default server to be used for finding the local IP address 20 | const rootServer = "198.41.0.4" 21 | 22 | /* 23 | Returns the local IP address. 24 | */ 25 | func getLocalIp() (*string, error) { 26 | conn, err := net.Dial("udp", rootServer+":1") 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | // The method call: conn.LocalAddr().String() returns ip_address:port 32 | return &strings.Split(conn.LocalAddr().String(), ":")[0], nil 33 | } 34 | 35 | func main() { 36 | var configPath string 37 | flag.StringVar(&configPath, 38 | "conf", 39 | "", "Path of the YAML configuration of the co-located DEA.") 40 | flag.Parse() 41 | 42 | config, err := common.ConfigFromFile(configPath) 43 | if err != nil { 44 | panic(err.Error()) 45 | } 46 | 47 | common.SetupSteno(&config.Server.Logging) 48 | log := steno.NewLogger("runner") 49 | 50 | var localIp *string 51 | localIp, err = getLocalIp() 52 | 53 | if err != nil { 54 | log.Fatal(err.Error()) 55 | } 56 | 57 | if err := directoryserver.Start(*localIp, config); err != nil { 58 | log.Fatal(err.Error()) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/container/warden_client_provider.rb: -------------------------------------------------------------------------------- 1 | require "em/warden/client" 2 | 3 | class WardenClientProvider 4 | attr_reader :socket_path 5 | 6 | def initialize(socket_path) 7 | @socket_path = socket_path 8 | @clients = {} 9 | end 10 | 11 | def get(name) 12 | client = @clients[name] 13 | 14 | return client if client && client.connected? 15 | new_client = EventMachine::Warden::FiberAwareClient.new(@socket_path) 16 | new_client.connect 17 | @clients[name] = new_client 18 | new_client 19 | end 20 | 21 | def close_all 22 | @clients.values.each(&:disconnect) 23 | end 24 | end -------------------------------------------------------------------------------- /lib/dea.rb: -------------------------------------------------------------------------------- 1 | module Dea 2 | end 3 | -------------------------------------------------------------------------------- /lib/dea/directory_server/hmac_helper.rb: -------------------------------------------------------------------------------- 1 | require "openssl" 2 | 3 | class HMACHelper 4 | attr_reader :key 5 | 6 | def initialize(key) 7 | raise ArgumentError, "key must not be nil" unless key 8 | @key = key 9 | end 10 | 11 | def create(str) 12 | hmac = OpenSSL::HMAC.new(@key, OpenSSL::Digest::SHA512.new) 13 | hmac.update(str) 14 | hmac.hexdigest 15 | end 16 | 17 | def compare(correct_hmac, str) 18 | generated_hmac = create(str) 19 | 20 | # Use constant_time_compare instead of simple '==' 21 | # to prevent possible timing attacks. 22 | # (http://codahale.com/a-lesson-in-timing-attacks/) 23 | constant_time_compare(correct_hmac, generated_hmac) 24 | end 25 | 26 | private 27 | 28 | def constant_time_compare(str1, str2) 29 | return false if str1.to_s.size != str2.to_s.size 30 | 31 | verified = true 32 | str1.to_s.bytes.zip(str2.to_s.bytes) do |expected_byte, given_byte| 33 | verified = false if expected_byte != given_byte 34 | end 35 | 36 | verified 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/dea/droplet_registry.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "dea/droplet" 4 | require "steno" 5 | require "steno/core_ext" 6 | 7 | module Dea 8 | class DropletRegistry < Hash 9 | attr_reader :base_dir 10 | 11 | def initialize(base_dir) 12 | super() do |hash, sha1| 13 | raise ArgumentError, "sha cannot be nil" if sha1.nil? 14 | logger.debug("New droplet: #{sha1}", droplet_sha1: sha1) 15 | 16 | hash[sha1] = Droplet.new(base_dir, sha1) 17 | end 18 | 19 | # Seed registry with available droplets 20 | Dir[File.join(base_dir, "*")].each do |path| 21 | self[File.basename(path)] 22 | end 23 | 24 | @base_dir = base_dir 25 | end 26 | 27 | private 28 | 29 | def logger 30 | @logger ||= self.class.logger 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/dea/env/exporter.rb: -------------------------------------------------------------------------------- 1 | module Dea 2 | class Env 3 | class Exporter < Struct.new(:variables) 4 | def export 5 | variables.map do |(key, value)| 6 | key = key.to_s 7 | value = value.to_s 8 | if key == "DATABASE_URL" || key == "VCAP_SERVICES" || key == "VCAP_APPLICATION" 9 | %Q{export %s=%s;\n} % [key, Shellwords.shellescape(value)] 10 | else 11 | %Q{export %s="%s";\n} % [key, value.gsub('"', '\"')] 12 | end 13 | end.join 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/dea/env/strategy_chooser.rb: -------------------------------------------------------------------------------- 1 | require "dea/staging/staging_message" 2 | require "dea/staging/env" 3 | require "dea/starting/env" 4 | 5 | module Dea 6 | class Env 7 | class StrategyChooser 8 | def initialize(message, task) 9 | @message = message 10 | @task = task 11 | end 12 | 13 | def strategy 14 | if @message.is_a? StagingMessage 15 | Staging::Env.new(@message, @task) 16 | else 17 | Starting::Env.new(@message, @task) 18 | end 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/dea/health_check/base.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "eventmachine" 4 | 5 | module Dea 6 | module HealthCheck 7 | class Base 8 | include ::EM::Deferrable 9 | 10 | def initialize 11 | setup_callbacks 12 | @done = false 13 | end 14 | 15 | def done? 16 | @done 17 | end 18 | 19 | private 20 | 21 | def setup_callbacks 22 | [:callback, :errback].each do |method| 23 | send(method) { @done = true } 24 | send(method) { cleanup } 25 | end 26 | end 27 | 28 | # Can potentially be called more than once, so make it idempotent. 29 | def cleanup 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/dea/health_check/port_open.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "dea/health_check/base" 4 | 5 | module Dea 6 | module HealthCheck 7 | class PortOpen < ::Dea::HealthCheck::Base 8 | 9 | class ConnectionNotifier < ::EM::Connection 10 | 11 | attr_reader :deferrable 12 | 13 | def initialize(deferrable) 14 | super 15 | 16 | @connection_completed = false 17 | 18 | @deferrable = deferrable 19 | end 20 | 21 | def connection_completed 22 | @connection_completed = true 23 | 24 | deferrable.succeed 25 | end 26 | 27 | def unbind 28 | # ECONNREFUSED, ECONNRESET, etc. 29 | deferrable.mark_failure unless @connection_completed 30 | end 31 | end 32 | 33 | attr_reader :host 34 | attr_reader :port 35 | 36 | def initialize(host, port, retry_interval_secs = 0.5) 37 | super() 38 | 39 | @host = host 40 | @port = port 41 | @timer = nil 42 | @retry_interval_secs = retry_interval_secs 43 | 44 | yield self if block_given? 45 | 46 | ::EM.next_tick { attempt_connect } 47 | end 48 | 49 | def mark_failure 50 | @timer = ::EM::Timer.new(@retry_interval_secs) { attempt_connect } 51 | end 52 | 53 | private 54 | 55 | def attempt_connect 56 | @conn = ::EM.connect(host, port, ConnectionNotifier, self) unless done? 57 | end 58 | 59 | def cleanup 60 | @conn.close_connection 61 | 62 | if @timer 63 | @timer.cancel 64 | @timer = nil 65 | end 66 | end 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /lib/dea/health_check/state_file_ready.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "steno/core_ext" 4 | require "yajl" 5 | 6 | require "dea/health_check/base" 7 | 8 | module Dea 9 | module HealthCheck 10 | class StateFileReady < ::Dea::HealthCheck::Base 11 | attr_reader :path 12 | 13 | def initialize(path, retry_interval_secs = 0.5) 14 | super() 15 | @path = path 16 | 17 | yield self if block_given? 18 | 19 | @timer = ::EM::PeriodicTimer.new(retry_interval_secs) do 20 | check_state_file 21 | end 22 | 23 | check_state_file 24 | end 25 | 26 | private 27 | 28 | def check_state_file 29 | return unless File.exists?(path) 30 | 31 | state = Yajl::Parser.parse(File.read(path)) 32 | if state && state["state"] == "RUNNING" 33 | succeed 34 | end 35 | 36 | rescue => e 37 | logger.error("Failed parsing state file: #{e}") 38 | logger.log_exception(e) 39 | # Ignore errors, health check will time out if errors persist. 40 | end 41 | 42 | def cleanup 43 | if @timer 44 | @timer.cancel 45 | @timer = nil 46 | end 47 | end 48 | 49 | def logger 50 | self.class.logger 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/dea/http/app_paths.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "grape" 4 | require "steno" 5 | 6 | class Dea::Http 7 | class AppPaths < Grape::API 8 | class << self 9 | def configure(bootstrap) 10 | global_setting(:bootstrap, bootstrap) 11 | end 12 | end 13 | 14 | version 'v1' 15 | content_type :json, 'application/json' 16 | format :json 17 | 18 | helpers do 19 | def json_error!(msg, status) 20 | error!({ "error" => msg }, status) 21 | end 22 | end 23 | 24 | def logger 25 | global_setting(:bootstrap).logger 26 | end 27 | 28 | resource :stage do 29 | post do 30 | data = env[Grape::Env::API_REQUEST_BODY] 31 | logger.debug('http.request.received', route: route.to_s, body: data) 32 | 33 | return status 503 if global_setting(:bootstrap).evac_handler.evacuating? || global_setting(:bootstrap).shutdown_handler.shutting_down? 34 | 35 | global_setting(:bootstrap).stage_app_request(data) 36 | 37 | status 202 38 | end 39 | end 40 | 41 | resource :apps do 42 | post do 43 | data = env[Grape::Env::API_REQUEST_BODY] 44 | logger.debug('http.request.received', route: route.to_s, body: data) 45 | 46 | return status 503 if global_setting(:bootstrap).evac_handler.evacuating? || global_setting(:bootstrap).shutdown_handler.shutting_down? 47 | 48 | global_setting(:bootstrap).start_app(data) 49 | 50 | status 202 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/dea/http/httpserver.rb: -------------------------------------------------------------------------------- 1 | require 'dea/http/app_paths' 2 | 3 | module Dea 4 | class HttpServer 5 | attr_reader :http_server, :port 6 | 7 | def initialize(bootstrap, config) 8 | ssl = config['ssl'] 9 | if ssl 10 | raise ArgumentError, "port must be configured" unless ssl['port'] 11 | @port = ssl['port'] 12 | 13 | Dea::Http::AppPaths.configure(bootstrap) 14 | 15 | helper_app = Class.new(Grape::API) do 16 | mount Dea::Http::AppPaths 17 | end 18 | 19 | Thin::Logging.silent = true 20 | 21 | @http_server = 22 | Thin::Server.new('0.0.0.0', port, helper_app, { signals: false }) 23 | 24 | @http_server.ssl = true 25 | @http_server.ssl_options = { 26 | private_key_file: ssl['key_file'], 27 | cert_chain_file: ssl['cert_file'], 28 | verify_peer: true, 29 | } 30 | end 31 | end 32 | 33 | def enabled? 34 | !@http_server.nil? 35 | end 36 | 37 | def start 38 | @http_server.start if @http_server 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/dea/lifecycle/evacuation_handler.rb: -------------------------------------------------------------------------------- 1 | class EvacuationHandler 2 | EXIT_REASON_EVACUATION = "DEA_EVACUATION" 3 | 4 | def initialize(bootstrap, message_bus, locator_responders, instance_registry, staging_task_registry, logger, config) 5 | @bootstrap = bootstrap 6 | @message_bus = message_bus 7 | @locator_responders = locator_responders 8 | @instance_registry = instance_registry 9 | @staging_task_registry = staging_task_registry 10 | @logger = logger 11 | @evacuation_bail_out_time_in_seconds = config["evacuation_bail_out_time_in_seconds"] 12 | end 13 | 14 | def evacuate!(goodbye_message) 15 | first_time = !@evacuation_processed 16 | @evacuation_processed = true 17 | 18 | @start_time ||= Time.now 19 | 20 | send_shutdown_and_stop_advertising(goodbye_message) if first_time 21 | transition_instances_to_evacuating(first_time) 22 | 23 | can_shutdown = dea_can_shutdown? 24 | logger.info("Evacuating (first time: #{first_time}; can shutdown: #{can_shutdown})") 25 | can_shutdown 26 | end 27 | 28 | def evacuating? 29 | @evacuation_processed 30 | end 31 | 32 | private 33 | 34 | def transition_instances_to_evacuating(first_time) 35 | @instance_registry.each do |instance| 36 | if instance.born? || instance.starting? || instance.resuming? || instance.running? 37 | @logger.error("Found an unexpected #{instance.state} instance while evacuating") unless first_time 38 | instance.state = Dea::Instance::State::EVACUATING 39 | end 40 | end 41 | @bootstrap.send_heartbeat 42 | EM.cancel_timer(@bootstrap.heartbeat_timer) if first_time 43 | end 44 | 45 | def send_shutdown_and_stop_advertising(goodbye_message) 46 | @locator_responders.map(&:stop) 47 | @message_bus.publish("dea.shutdown", goodbye_message) 48 | end 49 | 50 | def dea_can_shutdown? 51 | no_instances = @instance_registry.all? do |instance| 52 | instance.stopping? || instance.stopped? || instance.crashed? 53 | end 54 | 55 | (no_instances && @staging_task_registry.tasks.empty?) || (@start_time + @evacuation_bail_out_time_in_seconds <= Time.now) 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/dea/lifecycle/shutdown_handler.rb: -------------------------------------------------------------------------------- 1 | class ShutdownHandler 2 | def initialize(message_bus, locator_responders, instance_registry, staging_registry, droplet_registry, directory_server, logger) 3 | @message_bus = message_bus 4 | @locator_responders = locator_responders 5 | @instance_registry = instance_registry 6 | @staging_registry = staging_registry 7 | @droplet_registry = droplet_registry 8 | @directory_server = directory_server 9 | @logger = logger 10 | end 11 | 12 | def shutdown!(goodbye_message) 13 | return if shutting_down? 14 | @shutting_down = true 15 | 16 | remove_droplets 17 | @message_bus.publish("dea.shutdown", goodbye_message) 18 | @locator_responders.each(&:stop) 19 | @message_bus.stop 20 | @directory_server.unregister 21 | 22 | tasks = Set.new(@instance_registry.instances + @staging_registry.tasks) 23 | flush_message_bus_and_terminate if tasks.empty? 24 | 25 | tasks.dup.each do |task| 26 | task.stop do |error| 27 | tasks.delete(task) 28 | 29 | if error 30 | task.logger.warn("task failed to stop: #{error}") 31 | else 32 | task.logger.debug("task exited") 33 | end 34 | 35 | flush_message_bus_and_terminate if tasks.empty? 36 | end 37 | end 38 | end 39 | 40 | def shutting_down? 41 | @shutting_down 42 | end 43 | 44 | private 45 | 46 | def flush_message_bus_and_terminate 47 | @logger.info("All instances and staging tasks stopped, exiting.") 48 | @message_bus.flush { terminate } 49 | end 50 | 51 | def remove_droplets 52 | @droplet_registry.keys.each do |sha| 53 | logger.debug("Removing droplet for sha=#{sha}") 54 | 55 | @droplet_registry[sha].destroy 56 | end 57 | end 58 | 59 | # So we can test shutdown() 60 | def terminate 61 | exit 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/dea/lifecycle/signal_handler.rb: -------------------------------------------------------------------------------- 1 | require "dea/lifecycle/evacuation_handler" 2 | require "dea/lifecycle/shutdown_handler" 3 | 4 | class SignalHandler 5 | SIGNALS_OF_INTEREST = %W[TERM INT QUIT USR1 USR2].freeze 6 | 7 | def initialize(uuid, local_ip, message_bus, locator_responders, instance_registry, evac_handler, shutdown_handler, logger) 8 | @uuid = uuid 9 | @local_ip = local_ip 10 | 11 | @message_bus = message_bus 12 | @locator_responders = locator_responders 13 | @instance_registry = instance_registry 14 | @evac_handler = evac_handler 15 | @shutdown_handler = shutdown_handler 16 | @logger = logger 17 | end 18 | 19 | def setup(&kernel_trap) 20 | SIGNALS_OF_INTEREST.each do |signal| 21 | kernel_trap.call(signal) do 22 | safely do 23 | @logger.warn("caught SIG#{signal}") 24 | send("trap_#{signal.downcase}") 25 | end 26 | end 27 | end 28 | end 29 | 30 | private 31 | 32 | def safely 33 | Thread.new do 34 | EM.schedule do 35 | yield 36 | end 37 | end 38 | end 39 | 40 | def trap_term 41 | shutdown 42 | end 43 | 44 | def trap_int 45 | shutdown 46 | end 47 | 48 | def trap_quit 49 | shutdown 50 | end 51 | 52 | def trap_usr1 53 | @message_bus.publish("dea.shutdown", goodbye_message) 54 | @locator_responders.each(&:stop) 55 | end 56 | 57 | def trap_usr2 58 | evacuate unless @evac_handler.evacuating? 59 | end 60 | 61 | def evacuate 62 | can_shutdown = @evac_handler.evacuate!(goodbye_message) 63 | 64 | if can_shutdown 65 | shutdown 66 | else 67 | EM.add_timer(5) { evacuate } 68 | end 69 | end 70 | 71 | def shutdown 72 | @shutdown_handler.shutdown!(goodbye_message) 73 | end 74 | 75 | def goodbye_message 76 | Dea::Protocol::V1::GoodbyeMessage.generate(@uuid, @local_ip, @instance_registry) 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /lib/dea/loggregator.rb: -------------------------------------------------------------------------------- 1 | module Dea 2 | class Loggregator 3 | def self.log_error(e) 4 | logger.warn("Failed to emit loggregator message. #{e.message}") 5 | end 6 | 7 | @@emitter = nil 8 | @@staging_emitter = nil 9 | 10 | def self.emit(app_id, message) 11 | @@emitter.emit(app_id, message) if @@emitter 12 | rescue *Errno.constants.map{|e| Errno.const_get(e)} => e 13 | log_error(e) 14 | end 15 | 16 | def self.emit_error(app_id, message) 17 | @@emitter.emit_error(app_id, message) if @@emitter 18 | rescue *Errno.constants.map{|e| Errno.const_get(e)} => e 19 | log_error(e) 20 | end 21 | 22 | def self.emit_value(name, value, unit) 23 | @@emitter.emit_value_metric(name, value, unit) if @@emitter 24 | rescue *Errno.constants.map{|e| Errno.const_get(e)} => e 25 | log_error(e) 26 | end 27 | 28 | def self.emit_counter(name, delta) 29 | @@emitter.emit_counter(name, delta) if @@emitter 30 | rescue *Errno.constants.map{|e| Errno.const_get(e)} => e 31 | log_error(e) 32 | end 33 | 34 | def self.emit_container_metric(app_id, instanceIndex, cpuPercentage, memoryBytes, diskBytes) 35 | @@emitter.emit_container_metric(app_id, instanceIndex, cpuPercentage, memoryBytes, diskBytes) if @@emitter 36 | rescue *Errno.constants.map{|e| Errno.const_get(e)} => e 37 | log_error(e) 38 | end 39 | 40 | def self.emitter=(emitter) 41 | @@emitter = emitter 42 | end 43 | 44 | def self.staging_emitter=(emitter) 45 | @@staging_emitter = emitter 46 | end 47 | 48 | def self.staging_emit(app_id, message) 49 | @@staging_emitter.emit(app_id, message) if @@staging_emitter 50 | rescue *Errno.constants.map{|e| Errno.const_get(e)} => e 51 | log_error(e) 52 | end 53 | 54 | def self.staging_emit_error(app_id, message) 55 | @@staging_emitter.emit_error(app_id, message) if @@staging_emitter 56 | rescue *Errno.constants.map{|e| Errno.const_get(e)} => e 57 | log_error(e) 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /lib/dea/pid_file.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | 3 | module Dea 4 | def self.process_running?(pid) 5 | return false unless pid && (pid > 0) 6 | 7 | output = %x[ps -o rss= -p #{pid}] 8 | return true if ($? == 0 && !output.empty?) 9 | 10 | return false 11 | end 12 | 13 | class PidFile 14 | class ProcessRunningError < StandardError 15 | end 16 | 17 | def initialize(pid_file, create_parents=true) 18 | @pid_file = pid_file 19 | @dirty = true 20 | write(create_parents) 21 | end 22 | 23 | def unlink() 24 | return unless @dirty 25 | 26 | # Swallowing exception here is fine. Removing the pid files is a courtesy. 27 | begin 28 | File.unlink(@pid_file) 29 | @dirty = false 30 | rescue 31 | end 32 | self 33 | end 34 | 35 | def unlink_at_exit() 36 | at_exit { unlink() } 37 | self 38 | end 39 | 40 | protected 41 | 42 | # Atomically writes the pidfile. 43 | # This throws exceptions if the pidfile contains the pid of another running process. 44 | # 45 | # +create_parents+ If true, all parts of the path up to the file's dirname will be created. 46 | # 47 | def write(create_parents=true) 48 | FileUtils.mkdir_p(File.dirname(@pid_file)) if create_parents 49 | 50 | # Closing the fd as the block finishes releases our lock 51 | File.open(@pid_file, 'a+b', 0644) do |f| 52 | f.flock(File::LOCK_EX) 53 | 54 | # Check if process is already running 55 | pid = f.read().strip().to_i() 56 | if pid == Process.pid() 57 | return 58 | elsif Dea.process_running?(pid) 59 | raise ProcessRunningError.new("Process already running (pid=%d)." % (pid)) 60 | end 61 | 62 | f.truncate(0) 63 | f.rewind() 64 | f.write("%d\n" % (Process.pid())) 65 | f.flush() 66 | end 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /lib/dea/registry_enumeration.rb: -------------------------------------------------------------------------------- 1 | module Dea 2 | module RegistryEnumeration 3 | def reserved_memory_bytes 4 | reduce(0) do |sum, task| 5 | sum + (task.consuming_memory? ? task.memory_limit_in_bytes : 0) 6 | end 7 | end 8 | 9 | def used_memory_bytes 10 | reduce(0) { |sum, task| sum + task.used_memory_in_bytes } 11 | end 12 | 13 | def reserved_disk_bytes 14 | reduce(0) do |sum, task| 15 | sum + (task.consuming_disk? ? task.disk_limit_in_bytes : 0) 16 | end 17 | end 18 | end 19 | end -------------------------------------------------------------------------------- /lib/dea/responders/dea_locator.rb: -------------------------------------------------------------------------------- 1 | require "dea/protocol" 2 | 3 | module Dea::Responders 4 | class DeaLocator 5 | DEFAULT_ADVERTISE_INTERVAL = 5 6 | 7 | attr_reader :nats, :dea_id, :resource_manager 8 | attr_reader :stacks, :zone, :url 9 | 10 | 11 | def initialize(nats, dea_id, resource_manager, config, url) 12 | @nats = nats 13 | @dea_id = dea_id 14 | @resource_manager = resource_manager 15 | @stacks = config["stacks"].map { |stack| stack['name'] } || [] 16 | @zone = config["placement_properties"]["zone"] 17 | @advertise_interval = config["intervals"]["advertise"] || DEFAULT_ADVERTISE_INTERVAL 18 | @url = url 19 | end 20 | 21 | def start 22 | start_periodic_dea_advertise 23 | end 24 | 25 | def stop 26 | stop_periodic_dea_advertise 27 | end 28 | 29 | def advertise 30 | nats.publish( 31 | "dea.advertise", 32 | Dea::Protocol::V1::AdvertiseMessage.generate( 33 | id: dea_id, 34 | url: url, 35 | stacks: stacks, 36 | available_memory: resource_manager.remaining_memory, 37 | available_disk: resource_manager.remaining_disk, 38 | app_id_to_count: resource_manager.app_id_to_count, 39 | placement_zone: zone, 40 | ), 41 | ) 42 | rescue => e 43 | logger.error("dea_locator.advertise", error: e, backtrace: e.backtrace) 44 | end 45 | 46 | private 47 | 48 | # Cloud controller uses dea.advertise to 49 | # keep track of all deas that it can use to run apps 50 | def start_periodic_dea_advertise 51 | @advertise_timer = EM.add_periodic_timer(@advertise_interval) { advertise } 52 | end 53 | 54 | def stop_periodic_dea_advertise 55 | EM.cancel_timer(@advertise_timer) if @advertise_timer 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/dea/responders/http_staging.rb: -------------------------------------------------------------------------------- 1 | require 'dea/staging/staging_task' 2 | require 'dea/loggregator' 3 | 4 | module Dea::Responders 5 | class HttpStaging 6 | 7 | def initialize(stager, cc_client) 8 | @stager = stager 9 | @cc_client = cc_client 10 | end 11 | 12 | def handle(request) 13 | message = StagingMessage.new(request) 14 | message.set_responder do |params, &blk| 15 | @cc_client.send_staging_response(params) { blk.call if blk } 16 | end 17 | 18 | task = @stager.create_task(message) 19 | return unless task 20 | 21 | task.start 22 | rescue => e 23 | logger.error('staging.handle.http.failed', error: e, backtrace: e.backtrace) 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/dea/responders/nats_staging.rb: -------------------------------------------------------------------------------- 1 | require 'dea/staging/staging_task' 2 | require 'dea/loggregator' 3 | 4 | module Dea::Responders 5 | class NatsStaging 6 | def initialize(nats, dea_id, stager, config) 7 | @nats = nats 8 | @dea_id = dea_id 9 | @stager = stager 10 | @config = config 11 | end 12 | 13 | def start 14 | return unless configured_to_stage? 15 | subscribe_to_dea_specific_staging 16 | subscribe_to_staging_stop 17 | end 18 | 19 | def stop 20 | unsubscribe_from_dea_specific_staging 21 | unsubscribe_from_staging_stop 22 | end 23 | 24 | def handle(request) 25 | message = StagingMessage.new(request.data) 26 | message.set_responder do |params, &blk| 27 | request.respond(params) { blk.call if blk } 28 | end 29 | 30 | task = @stager.create_task(message) 31 | return unless task 32 | 33 | notify_setup_completion(request, task) 34 | 35 | task.start 36 | rescue => e 37 | logger.error('staging.handle.failed', error: e, backtrace: e.backtrace) 38 | end 39 | 40 | def handle_stop(message) 41 | @stager.stop_task(message.data['app_id']) 42 | end 43 | 44 | private 45 | 46 | def configured_to_stage? 47 | @config['staging'] && @config['staging']['enabled'] 48 | end 49 | 50 | def subscribe_to_dea_specific_staging 51 | @dea_specified_staging_sid = 52 | @nats.subscribe("staging.#{@dea_id}.start", {do_not_track_subscription: true}) { |request| handle(request) } 53 | end 54 | 55 | def unsubscribe_from_dea_specific_staging 56 | @nats.unsubscribe(@dea_specified_staging_sid) if @dea_specified_staging_sid 57 | end 58 | 59 | def subscribe_to_staging_stop 60 | @staging_stop_sid = 61 | @nats.subscribe('staging.stop', {do_not_track_subscription: true}) { |request| handle_stop(request) } 62 | end 63 | 64 | def unsubscribe_from_staging_stop 65 | @nats.unsubscribe(@staging_stop_sid) if @staging_stop_sid 66 | end 67 | 68 | def notify_setup_completion(request, task) 69 | task.after_setup_callback do |error| 70 | request.respond({ 71 | task_id: task.task_id, 72 | task_streaming_log_url: task.streaming_log_url, 73 | error: (error.to_s if error) 74 | }) 75 | end 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /lib/dea/router_client.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | module Dea 4 | class RouterClient 5 | attr_reader :bootstrap 6 | 7 | def initialize(bootstrap) 8 | @bootstrap = bootstrap 9 | end 10 | 11 | def register_directory_server(port, uri) 12 | req = generate_directory_server_request(port, uri) 13 | bootstrap.nats.publish("router.register", req) 14 | end 15 | 16 | def unregister_directory_server(port, uri) 17 | req = generate_directory_server_request(port, uri) 18 | bootstrap.nats.publish("router.unregister", req) 19 | end 20 | 21 | def register_instance(instance, opts = {}) 22 | req = generate_instance_request(instance, opts) 23 | bootstrap.nats.publish("router.register", req) 24 | end 25 | 26 | def unregister_instance(instance, opts = {}) 27 | req = generate_instance_request(instance, opts) 28 | bootstrap.nats.publish("router.unregister", req) 29 | end 30 | 31 | def greet(&blk) 32 | bootstrap.nats.request("router.greet", &blk) 33 | end 34 | 35 | private 36 | 37 | # Same format is used for both registration and unregistration 38 | def generate_instance_request(instance, opts = {}) 39 | { "dea" => bootstrap.uuid, 40 | "app" => instance.application_id, 41 | "uris" => opts[:uris] || instance.application_uris, 42 | "host" => bootstrap.local_ip, 43 | "port" => instance.instance_host_port, 44 | "tags" => { "component" => "dea-#{bootstrap.config["index"]}" }, 45 | "private_instance_id" => instance.private_instance_id, 46 | } 47 | end 48 | 49 | # Same format is used for both registration and unregistration 50 | def generate_directory_server_request(port, uri) 51 | { "host" => bootstrap.local_ip, 52 | "port" => port, 53 | "uris" => [uri], 54 | "tags" => { "component" => "directory-server-#{bootstrap.config["index"]}" }, 55 | } 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/dea/staging/admin_buildpack_downloader.rb: -------------------------------------------------------------------------------- 1 | require "dea/promise" 2 | require "dea/utils/download" 3 | require "dea/utils/non_blocking_unzipper" 4 | require "em-synchrony/thread" 5 | 6 | class AdminBuildpackDownloader 7 | attr_reader :logger 8 | 9 | DOWNLOAD_MUTEX = EventMachine::Synchrony::Thread::Mutex.new 10 | MAX_DOWNLOAD_ATTEMPTS = 3 11 | 12 | def initialize(buildpacks, destination_directory, custom_logger=nil) 13 | @buildpacks = buildpacks 14 | @destination_directory = destination_directory 15 | @logger = custom_logger || self.class.logger 16 | end 17 | 18 | def download 19 | logger.debug("admin-buildpacks.download", buildpacks: @buildpacks) 20 | return unless @buildpacks 21 | 22 | FileUtils.mkdir_p(@destination_directory) 23 | DOWNLOAD_MUTEX.synchronize do 24 | @buildpacks.each do |buildpack| 25 | attempts = 0 26 | success = false 27 | 28 | dest_dir = File.join(@destination_directory, buildpack.fetch(:key)) 29 | unless File.exists?(dest_dir) 30 | until success 31 | begin 32 | attempts += 1 33 | download_one_buildpack(buildpack, dest_dir).resolve 34 | success = true 35 | rescue Download::DownloadError => e 36 | raise e if attempts >= MAX_DOWNLOAD_ATTEMPTS 37 | end 38 | end 39 | end 40 | end 41 | end 42 | end 43 | 44 | private 45 | 46 | def download_one_buildpack(buildpack, dest_dir) 47 | Dea::Promise.new do |p| 48 | tmpfile = Tempfile.new('temp_admin_buildpack') 49 | 50 | Download.new(buildpack.fetch(:url), tmpfile, nil, logger).download! do |err| 51 | if err 52 | p.fail err 53 | else 54 | NonBlockingUnzipper.new.unzip_to_folder(tmpfile.path, dest_dir) do |output, status| 55 | tmpfile.unlink 56 | if status == 0 57 | p.deliver 58 | else 59 | p.fail output 60 | end 61 | end 62 | end 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/dea/staging/buildpack_manager.rb: -------------------------------------------------------------------------------- 1 | require "dea/staging/admin_buildpack_downloader" 2 | 3 | module Dea 4 | class BuildpackManager 5 | def initialize (admin_buildpacks_dir, staging_message, buildpacks_in_use) 6 | @admin_buildpacks_dir = admin_buildpacks_dir 7 | @staging_message = staging_message 8 | @buildpacks_in_use = buildpacks_in_use 9 | end 10 | 11 | def download 12 | AdminBuildpackDownloader.new(@staging_message.admin_buildpacks, @admin_buildpacks_dir).download 13 | end 14 | 15 | def clean 16 | buildpack_paths_needing_deletion.each do |path| 17 | FileUtils.rm_rf(path) 18 | end 19 | end 20 | 21 | def buildpack_dirs 22 | admin_buildpack_paths.map(&:to_s) 23 | end 24 | 25 | def buildpack_key(buildpack_dir) 26 | return nil unless buildpack_dir 27 | path = Pathname.new(buildpack_dir) 28 | return nil unless admin_buildpack_path?(path) 29 | path.basename.to_s 30 | end 31 | 32 | private 33 | 34 | def buildpack_paths_needing_deletion 35 | local_admin_buildpack_paths - (admin_buildpack_paths + buildpacks_in_use_paths) 36 | end 37 | 38 | def buildpacks_in_use_paths 39 | @buildpacks_in_use.map { |b| Pathname.new(@admin_buildpacks_dir).join(b[:key]) } 40 | end 41 | 42 | def admin_buildpack_paths 43 | @staging_message.admin_buildpacks.map do |buildpack| 44 | File.join(@admin_buildpacks_dir, buildpack[:key]) 45 | end.select { |dir| File.exists?(dir) }.map do |dir| 46 | Pathname.new(dir) 47 | end 48 | end 49 | 50 | def local_admin_buildpack_paths 51 | Pathname.new(@admin_buildpacks_dir).children 52 | end 53 | 54 | def admin_buildpack_path?(path) 55 | local_admin_buildpack_paths.include?(path) 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/dea/staging/buildpacks_message.rb: -------------------------------------------------------------------------------- 1 | class BuildpacksMessage 2 | def initialize(message) 3 | @message = message 4 | end 5 | 6 | def buildpacks 7 | (@message || []).map do |buildpack| 8 | begin 9 | { url: URI(buildpack["url"]), key: buildpack["key"] } 10 | rescue => e 11 | logger.log_exception(e) 12 | end 13 | end.compact 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/dea/staging/env.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | module Dea 4 | module Staging 5 | class Env 6 | attr_reader :message, :staging_task 7 | 8 | def initialize(message, staging_task) 9 | @message = message 10 | @staging_task = staging_task 11 | end 12 | 13 | def system_environment_variables 14 | [ 15 | ["STAGING_TIMEOUT", staging_task.staging_timeout], 16 | ["MEMORY_LIMIT", "#{message.mem_limit}m"] 17 | ] 18 | end 19 | 20 | def vcap_application 21 | {} 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/dea/staging/staging_message.rb: -------------------------------------------------------------------------------- 1 | require "dea/starting/start_message" 2 | require 'dea/staging/buildpacks_message' 3 | require "steno" 4 | require "steno/core_ext" 5 | 6 | class StagingMessage 7 | def initialize(message) 8 | @message = message 9 | end 10 | 11 | def to_hash 12 | @message 13 | end 14 | 15 | def app_id 16 | @message["app_id"] 17 | end 18 | 19 | def properties 20 | @message["properties"] || {} 21 | end 22 | 23 | def task_id 24 | @message["task_id"] 25 | end 26 | 27 | def download_uri 28 | URI(@message["download_uri"]) if @message["download_uri"] 29 | end 30 | 31 | def upload_uri 32 | URI(@message["upload_uri"]) if @message["upload_uri"] 33 | end 34 | 35 | def buildpack_cache_upload_uri 36 | URI(@message["buildpack_cache_upload_uri"]) if @message["buildpack_cache_upload_uri"] 37 | end 38 | 39 | def buildpack_cache_download_uri 40 | URI(@message["buildpack_cache_download_uri"]) if @message["buildpack_cache_download_uri"] 41 | end 42 | 43 | def accepts_http? 44 | @message['accepts_http'] || false 45 | end 46 | 47 | def start_message 48 | @start_message ||= StartMessage.new(@message["start_message"]) 49 | end 50 | 51 | def admin_buildpacks 52 | BuildpacksMessage.new(@message["admin_buildpacks"]).buildpacks 53 | end 54 | 55 | def buildpack_git_url 56 | url = properties["buildpack"] || properties["buildpack_git_url"] 57 | URI(url) if url 58 | end 59 | 60 | def buildpack_key 61 | properties["buildpack_key"] 62 | end 63 | 64 | def egress_rules 65 | @message["egress_network_rules"] || [] 66 | end 67 | 68 | def env 69 | properties["environment"] || [] 70 | end 71 | 72 | def services 73 | properties["services"] || [] 74 | end 75 | 76 | def vcap_application 77 | start_message.vcap_application 78 | end 79 | 80 | def mem_limit 81 | @message["memory_limit"] || start_message.mem_limit 82 | end 83 | 84 | def disk_limit 85 | @message["disk_limit"] || start_message.disk_limit 86 | end 87 | 88 | def stack 89 | @message['stack'] 90 | end 91 | 92 | def set_responder(&blk) 93 | @response_callback = blk 94 | end 95 | 96 | def respond(params, &blk) 97 | return if @response_callback.nil? 98 | blk ? @response_callback.call(params) {blk.call} : @response_callback.call(params) 99 | end 100 | 101 | private 102 | 103 | def logger 104 | self.class.logger 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /lib/dea/staging/staging_task_registry.rb: -------------------------------------------------------------------------------- 1 | require "dea/registry_enumeration" 2 | 3 | module Dea 4 | class StagingTaskRegistry 5 | include Enumerable 6 | include RegistryEnumeration 7 | 8 | def register(task) 9 | tasks_map[task.task_id] = task 10 | end 11 | 12 | def unregister(task) 13 | tasks_map.delete(task.task_id) 14 | end 15 | 16 | def registered_task(task_id) 17 | tasks_map[task_id] 18 | end 19 | 20 | def each(&block) 21 | tasks_map.each_value(&block) 22 | end 23 | 24 | def tasks 25 | tasks_map.values 26 | end 27 | 28 | private 29 | 30 | def tasks_map 31 | @tasks_map ||= {} 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/dea/starting/database_uri_generator.rb: -------------------------------------------------------------------------------- 1 | module Dea 2 | class DatabaseUriGenerator 3 | VALID_DB_TYPES = %w[mysql mysql2 postgres postgresql db2 informix].freeze 4 | RAILS_STYLE_DATABASE_TO_ADAPTER_MAPPING = { 5 | 'mysql' => 'mysql2', 6 | 'postgresql' => 'postgres', 7 | 'db2' => 'ibmdb', 8 | 'informix' => 'ibmdb' 9 | }.freeze 10 | 11 | def initialize(services) 12 | @services = Array(services).compact || [] 13 | end 14 | 15 | def database_uri 16 | bound_database_uri 17 | end 18 | 19 | private 20 | 21 | def bound_database_uri 22 | if bound_relational_valid_databases.any? 23 | bound_relational_valid_databases.first 24 | else 25 | nil 26 | end 27 | end 28 | 29 | def bound_relational_valid_databases 30 | @bound_relational_valid_databases ||= @services.inject([]) do |collection, binding| 31 | credentials = binding["credentials"] 32 | if credentials && credentials["uri"] 33 | (scheme, rest) = binding["credentials"]["uri"].split(":", 2) 34 | if scheme && rest 35 | collection << "#{convert_scheme_to_rails_style_adapter(scheme)}:#{rest}" if VALID_DB_TYPES.include?(scheme) 36 | end 37 | end 38 | collection 39 | end 40 | end 41 | 42 | def convert_scheme_to_rails_style_adapter(scheme) 43 | return RAILS_STYLE_DATABASE_TO_ADAPTER_MAPPING[scheme] if RAILS_STYLE_DATABASE_TO_ADAPTER_MAPPING[scheme] 44 | scheme 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/dea/starting/env.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | module Dea 4 | module Starting 5 | class Env 6 | attr_reader :message, :instance 7 | 8 | def initialize(message, instance) 9 | @message = message 10 | @instance = instance 11 | end 12 | 13 | def system_environment_variables 14 | [ 15 | ["HOME", "$PWD/app"], 16 | ["TMPDIR", "$PWD/tmp"], 17 | ["VCAP_APP_HOST", "0.0.0.0"], 18 | ["VCAP_APP_PORT", @instance.instance_container_port], 19 | ["PORT", "$VCAP_APP_PORT"], 20 | ["CF_INSTANCE_INDEX", @message.index], 21 | ["CF_INSTANCE_IP", @instance.bootstrap.local_ip], 22 | ["CF_INSTANCE_PORT", @instance.instance_container_port], 23 | ["CF_INSTANCE_ADDR", "#{@instance.bootstrap.local_ip}:#{@instance.instance_container_port}"], 24 | ["CF_INSTANCE_PORTS", %([{"external":#{@instance.instance_host_port},"internal":#{@instance.instance_container_port}}])] 25 | ] 26 | end 27 | 28 | def vcap_application 29 | start_time = Time.at(@instance.state_starting_timestamp) 30 | { 31 | "application_id" => @instance.attributes["application_id"], 32 | "instance_id" => @instance.attributes["instance_id"], 33 | "instance_index" => @message.index, 34 | "host" => "0.0.0.0", 35 | "port" => @instance.instance_container_port, 36 | "started_at" => start_time, 37 | "started_at_timestamp" => start_time.to_i, 38 | "start" => start_time, 39 | "state_timestamp" => start_time.to_i, 40 | } 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/dea/starting/instance_uri_updater.rb: -------------------------------------------------------------------------------- 1 | module Dea 2 | class InstanceUriUpdater 3 | def initialize(instance, uris) 4 | @instance = instance 5 | @uris = uris 6 | end 7 | 8 | def update(router_client) 9 | changed = false 10 | current_uris = @instance.application_uris 11 | 12 | logger.debug("Mapping new URIs") 13 | logger.debug("New: #{@uris} \n Old: #{current_uris}") 14 | 15 | new_uris = @uris - current_uris 16 | unless new_uris.empty? 17 | router_client.register_instance(@instance, :uris => new_uris) 18 | changed = true 19 | end 20 | 21 | obsolete_uris = current_uris - @uris 22 | unless obsolete_uris.empty? 23 | router_client.unregister_instance(@instance, :uris => obsolete_uris) 24 | changed = true 25 | end 26 | 27 | @instance.application_uris = @uris 28 | changed 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/dea/starting/start_message.rb: -------------------------------------------------------------------------------- 1 | class StartMessage 2 | def initialize(message) 3 | @message = message 4 | end 5 | 6 | def to_hash 7 | message 8 | end 9 | 10 | def index 11 | message["index"] 12 | end 13 | 14 | def droplet 15 | message["droplet"] 16 | end 17 | 18 | def version 19 | message["version"] 20 | end 21 | 22 | def name 23 | message["name"] 24 | end 25 | 26 | def uris 27 | (message["uris"] || []).map { |uri| URI(uri) } 28 | end 29 | 30 | def executable_uri 31 | URI(message["executableUri"]) if message["executableUri"] 32 | end 33 | 34 | def cc_partition 35 | message["cc_partition"] 36 | end 37 | 38 | def vcap_application 39 | message["vcap_application"] || {} 40 | end 41 | 42 | def limits 43 | message["limits"] || {} 44 | end 45 | 46 | def mem_limit 47 | limits["mem"] 48 | end 49 | 50 | def disk_limit 51 | limits["disk"] 52 | end 53 | 54 | def fds_limit 55 | limits["fds"] 56 | end 57 | 58 | def start_command 59 | message["start_command"] 60 | end 61 | 62 | def services 63 | message["services"] || [] 64 | end 65 | 66 | def debug 67 | !!message["debug"] 68 | end 69 | 70 | def sha1 71 | message["sha1"] 72 | end 73 | 74 | def console 75 | !!message["console"] 76 | end 77 | 78 | def executable_file 79 | message["executableFile"] 80 | end 81 | 82 | def env 83 | message["env"] || [] 84 | end 85 | 86 | def egress_network_rules 87 | message["egress_network_rules"] || [] 88 | end 89 | 90 | def stack 91 | message["stack"] 92 | end 93 | 94 | def message 95 | @message || {} 96 | end 97 | end 98 | -------------------------------------------------------------------------------- /lib/dea/starting/startup_script_generator.rb: -------------------------------------------------------------------------------- 1 | module Dea 2 | class StartupScriptGenerator 3 | def self.strip_heredoc(string) 4 | indent = string.scan(/^[ \t]*(?=\S)/).min.size 5 | string.gsub(/^[ \t]{#{indent}}/, '') 6 | end 7 | 8 | EXPORT_BUILDPACK_ENV_VARIABLES_SCRIPT = strip_heredoc(<<-BASH).freeze 9 | unset GEM_PATH 10 | if [ -d app/.profile.d ]; then 11 | for i in app/.profile.d/*.sh; do 12 | if [ -r $i ]; then 13 | . $i 14 | fi 15 | done 16 | unset i 17 | fi 18 | BASH 19 | 20 | START_SCRIPT = strip_heredoc(<<-BASH).freeze 21 | DROPLET_BASE_DIR=$PWD 22 | cd app 23 | echo $$ >> $DROPLET_BASE_DIR/run.pid 24 | exec bash -c %s 25 | BASH 26 | 27 | def initialize(start_command, user_envs, system_envs, post_setup_hook) 28 | @start_command = start_command 29 | @user_envs = user_envs 30 | @system_envs = system_envs 31 | @post_setup_hook = post_setup_hook 32 | end 33 | 34 | def generate 35 | script = [] 36 | script << "umask 077" 37 | script << @system_envs 38 | script << @user_envs 39 | script << EXPORT_BUILDPACK_ENV_VARIABLES_SCRIPT 40 | script << @post_setup_hook unless @post_setup_hook.nil? || @post_setup_hook == '' 41 | script << START_SCRIPT % Shellwords.shellescape(@start_command) 42 | script.join("\n") 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/dea/stat_collector.rb: -------------------------------------------------------------------------------- 1 | require 'dea/loggregator' 2 | 3 | module Dea 4 | class StatCollector 5 | INTERVAL = 10 6 | 7 | attr_reader :used_memory_in_bytes 8 | attr_reader :used_disk_in_bytes 9 | attr_reader :computed_pcpu 10 | attr_reader :computed_dcpu 11 | 12 | def initialize(container, application_id, instance_index) 13 | @container = container 14 | @application_id = application_id 15 | @instance_index = instance_index 16 | @cpu_samples = [] 17 | @computed_pcpu = 0 18 | @computed_dcpu = 0 19 | @used_memory_in_bytes = 0 20 | @used_disk_in_bytes = 0 21 | end 22 | 23 | def emit_metrics(now) 24 | return unless @container.handle 25 | 26 | info = @container.info 27 | rescue => e 28 | logger.error("stat-collector.info-retrieval.failed", handle: @container.handle, error: e, backtrace: e.backtrace) 29 | else 30 | @computed_pcpu = compute_cpu_usage(info.cpu_stat.usage, now) || 0 31 | @used_memory_in_bytes = compute_memory_usage(info.memory_stat) || 0 32 | @used_disk_in_bytes = info.disk_stat ? info.disk_stat.bytes_used : 0 33 | 34 | Dea::Loggregator.emit_container_metric( 35 | @application_id, @instance_index, @computed_pcpu, @used_memory_in_bytes, @used_disk_in_bytes) 36 | end 37 | 38 | private 39 | 40 | def compute_cpu_usage(usage, now) 41 | @cpu_samples << { 42 | :timestamp_ns => now.to_i * 1_000_000_000 + now.nsec, 43 | :ns_used => usage, 44 | } 45 | 46 | @cpu_samples.shift if @cpu_samples.size > 2 47 | 48 | if @cpu_samples.size == 2 49 | used = @cpu_samples[1][:ns_used] - @cpu_samples[0][:ns_used] 50 | elapsed = @cpu_samples[1][:timestamp_ns] - @cpu_samples[0][:timestamp_ns] 51 | 52 | if elapsed > 0 53 | @computed_dcpu = used.to_f / elapsed 54 | @computed_pcpu = (used * 100).to_f / elapsed 55 | end 56 | end 57 | end 58 | 59 | def compute_memory_usage(memory_stat) 60 | return memory_stat.total_rss + memory_stat.total_cache - memory_stat.total_inactive_file 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/dea/user_facing_errors.rb: -------------------------------------------------------------------------------- 1 | module Dea 2 | class UserFacingError < RuntimeError; end 3 | 4 | class HealthCheckFailed < UserFacingError 5 | def to_s 6 | "failed to accept connections within health check timeout" 7 | end 8 | end 9 | 10 | class MissingStartCommand < UserFacingError 11 | def to_s 12 | "missing start command" 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/dea/utils.rb: -------------------------------------------------------------------------------- 1 | require 'uuidtools' 2 | require 'socket' 3 | 4 | module Dea 5 | def self.local_ip(route = '1.2.3.4') 6 | orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true 7 | @local_ip = UDPSocket.open {|s| s.connect(route, 1); s.addr.last } 8 | ensure 9 | Socket.do_not_reverse_lookup = orig 10 | end 11 | 12 | def self.grab_ephemeral_port 13 | orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true 14 | socket = TCPServer.new('0.0.0.0', 0) 15 | socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true) 16 | port = socket.addr[1] 17 | socket.close 18 | return port 19 | ensure 20 | Socket.do_not_reverse_lookup = orig 21 | end 22 | 23 | def self.secure_uuid 24 | UUIDTools::UUID.random_create.to_s.delete('-') 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/dea/utils/download.rb: -------------------------------------------------------------------------------- 1 | require 'dea/utils/uri_cleaner' 2 | 3 | class Download 4 | attr_reader :source_uri, :destination_file, :sha1_expected 5 | attr_reader :logger 6 | 7 | class DownloadError < StandardError 8 | attr_reader :data 9 | 10 | def initialize(msg, data = {}) 11 | @data = data 12 | 13 | super("Error downloading: %s" % msg) 14 | end 15 | 16 | def uri 17 | data[:droplet_uri] || "(unknown)" 18 | end 19 | end 20 | 21 | def initialize(source_uri, destination_file, sha1_expected=nil, custom_logger=nil) 22 | @source_uri = source_uri 23 | @destination_file = destination_file 24 | @sha1_expected = sha1_expected 25 | @logger = custom_logger || self.class.logger 26 | end 27 | 28 | def download!(&blk) 29 | destination_file.binmode 30 | sha1 = Digest::SHA1.new 31 | 32 | http = EM::HttpRequest.new(source_uri, :inactivity_timeout => 30).get 33 | 34 | http.stream do |chunk| 35 | destination_file << chunk 36 | sha1 << chunk 37 | end 38 | 39 | context = {:droplet_uri => URICleaner.clean(source_uri)} 40 | 41 | http.errback do 42 | begin 43 | destination_file.close 44 | msg = "Response status: unknown, Error: #{http.error}" 45 | error = DownloadError.new(msg, context) 46 | logger.error("em-http-request.errored", error: error.message, data: error.data) 47 | blk.call(error) 48 | rescue => e 49 | logger.error("em-download.failed", error: e, backtrace: e.backtrace) 50 | end 51 | end 52 | 53 | http.callback do 54 | begin 55 | destination_file.close 56 | http_status = http.response_header.status 57 | 58 | context[:droplet_http_status] = http_status 59 | 60 | if http_status == 200 61 | sha1_actual = sha1.hexdigest 62 | if !sha1_expected || sha1_expected == sha1_actual 63 | logger.info("Download succeeded") 64 | blk.call(nil) 65 | else 66 | context[:droplet_sha1_expected] = sha1_expected 67 | context[:droplet_sha1_actual] = sha1_actual 68 | 69 | error = DownloadError.new("SHA1 mismatch", context) 70 | logger.warn(error.message, error.data) 71 | blk.call(error) 72 | end 73 | else 74 | error = DownloadError.new("HTTP status: #{http_status}", context) 75 | logger.warn(error.message, error.data) 76 | blk.call(error) 77 | end 78 | rescue => e 79 | logger.error("em-download.failed", error: e, backtrace: e.backtrace) 80 | end 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /lib/dea/utils/event_emitter.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | module Dea 4 | module EventEmitter 5 | def on(event, cb = nil, &blk) 6 | _listeners[event] << (cb || blk) 7 | end 8 | 9 | def emit(event, *args) 10 | _listeners[event].each { |l| l.call(*args) } 11 | end 12 | 13 | private 14 | 15 | def _listeners 16 | @_listeners ||= Hash.new { |h,k| h[k] = [] } 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/dea/utils/eventmachine_multipart_hack.rb: -------------------------------------------------------------------------------- 1 | require 'eventmachine' 2 | require 'em-http' 3 | require 'em-http/version' 4 | require 'active_support/core_ext/module/aliasing' 5 | 6 | raise "Upgrade hack if necessary" if EM::HttpRequest::VERSION != "1.1.3" 7 | 8 | EventMachine::HttpClient::MULTIPART_HACK = "x-cf-multipart".freeze 9 | 10 | EventMachine::HttpClient.class_eval do 11 | def send_request_with_multipart(head, body) 12 | if (multipart = head.delete(EventMachine::HttpClient::MULTIPART_HACK)) 13 | file = @req.file 14 | multipart_header = make_multipart_header(multipart[:name], multipart[:filename]) 15 | 16 | # We append as #stream_file_data closes the connection 17 | system "echo '#{EventMachine::HttpClient::CRLF}#{multipart_footer}' >> #{file}" 18 | 19 | @conn.send_data http_header(file, head, multipart_header) 20 | @conn.send_data multipart_header 21 | @conn.stream_file_data file, :http_chunks => false 22 | else 23 | send_request_without_multipart(head, body) 24 | end 25 | end 26 | 27 | alias_method_chain :send_request, :multipart 28 | 29 | private 30 | 31 | def make_multipart_header(name, filename) 32 | # TWO blank lines are needed at the end according to http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html 33 | [ 34 | "--#{boundary}", 35 | "Content-Disposition: form-data; name=\"#{name}\"; filename=\"#{filename}\"", 36 | "Content-Type: application/octet-stream", 37 | "", 38 | "" 39 | ].join(EventMachine::HttpClient::CRLF) 40 | end 41 | 42 | def multipart_footer 43 | "--#{boundary}--" 44 | end 45 | 46 | def boundary 47 | @boundary ||= "multipart-boundary-#{SecureRandom.uuid}" 48 | end 49 | 50 | def http_header(file, head, multipart_header) 51 | [ 52 | encode_request(@req.method, @req.uri, @req.query, @conn.connopts), 53 | encode_headers(head.merge( 54 | "content-type" => "multipart/form-data; boundary=#{boundary}", 55 | "content-length" => File.size(file) + multipart_header.length 56 | )), 57 | EventMachine::HttpClient::CRLF 58 | ].join('') 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /lib/dea/utils/hm9000.rb: -------------------------------------------------------------------------------- 1 | require 'dea/utils/uri_cleaner' 2 | require 'httpclient' 3 | 4 | class HM9000 5 | attr_reader :logger 6 | 7 | def initialize(destination, key_file, cert_file, ca_file, timeout, custom_logger=nil) 8 | @destination = URI.join(destination, '/dea/heartbeat') 9 | @logger = custom_logger || self.class.logger 10 | 11 | client = HTTPClient.new 12 | client.connect_timeout = 5 13 | client.receive_timeout = 5 14 | client.send_timeout = 5 15 | client.keep_alive_timeout = timeout 16 | 17 | ssl = client.ssl_config 18 | ssl.verify_mode = OpenSSL::SSL::VERIFY_PEER 19 | 20 | ssl.set_client_cert_file(cert_file, key_file) 21 | 22 | ssl.clear_cert_store 23 | ssl.add_trust_ca(ca_file) 24 | 25 | @http_client = client 26 | end 27 | 28 | def send_heartbeat(heartbeat, &callback) 29 | logger.debug('hm9000.heartbeat.sending', destination: URICleaner.clean(@destination)) 30 | 31 | connection = @http_client.post_async(@destination, header: { 'Content-Type' => 'application/json' }, body: Yajl::Encoder.encode(heartbeat)) 32 | EM.defer( 33 | lambda do 34 | begin 35 | response = connection.pop 36 | handle_http_response(response, callback) 37 | rescue => e 38 | logger.error('hm9000.heartbeat.failed', error: e) 39 | end 40 | end 41 | ) 42 | end 43 | 44 | private 45 | 46 | def handle_http_response(response, callback) 47 | http_status = response.status 48 | if http_status == 202 49 | logger.debug('hm9000.heartbeat.accepted') 50 | callback.call(response) if callback 51 | else 52 | handle_error(response, callback) 53 | end 54 | end 55 | 56 | 57 | def handle_error(response, callback) 58 | logger.warn( 59 | 'hm9000.heartbeat.failed', 60 | destination: URICleaner.clean(@destination), 61 | http_status: response.status, 62 | http_response: response.content 63 | ) 64 | 65 | callback.call(response) if callback 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /lib/dea/utils/non_blocking_unzipper.rb: -------------------------------------------------------------------------------- 1 | class NonBlockingUnzipper 2 | def unzip_to_folder(file, dest_dir, mode=0755) 3 | tmp_dir = Dir.mktmpdir 4 | File.chmod(mode, tmp_dir) 5 | EM.system "unzip -q #{file} -d #{tmp_dir}" do |output, status| 6 | if status.exitstatus == 0 7 | move_atomically(tmp_dir, dest_dir) 8 | else 9 | Dir.rmdir(tmp_dir) 10 | end 11 | 12 | yield output, status.exitstatus 13 | end 14 | end 15 | 16 | private 17 | 18 | def move_atomically(from_dir, dest_dir) 19 | tmp_dest_dir = File.join(File.dirname(dest_dir), File.basename(dest_dir) + "-moving") 20 | 21 | begin 22 | FileUtils.mv(from_dir, tmp_dest_dir) 23 | File.rename(tmp_dest_dir, dest_dir) 24 | rescue => e 25 | ## If the move fails half-way (which can happen when 26 | ## moving between filesystems) we'll be left with a 27 | ## directory named [guid]-moving, we'll try to clean 28 | ## this up here, but if it fails it should only result 29 | ## in some unnecesarily used disk-space until the next 30 | ## download for the same [guid] happens 31 | FileUtils.rm_rf(tmp_dest_dir) 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/dea/utils/sync_upload.rb: -------------------------------------------------------------------------------- 1 | require "dea/utils/eventmachine_multipart_hack" 2 | require "dea/utils/uri_cleaner" 3 | 4 | class UploadError < StandardError 5 | def initialize(msg) 6 | super("Error uploading: (#{msg})") 7 | end 8 | end 9 | 10 | class SyncUpload 11 | INACTIVITY_TIMEOUT = 30.freeze 12 | 13 | attr_reader :logger 14 | 15 | def initialize(source, destination, custom_logger=nil) 16 | @source = source 17 | @destination = destination 18 | @logger = custom_logger || self.class.logger 19 | end 20 | 21 | def upload!(&upload_callback) 22 | logger.info("em-upload.begin", destination: URICleaner.clean(@destination)) 23 | head = {EM::HttpClient::MULTIPART_HACK => {name: "upload[droplet]", filename: File.basename(@source)}} 24 | 25 | http = EM::HttpRequest.new(@destination, inactivity_timeout: INACTIVITY_TIMEOUT).post( 26 | head: head, file: @source, query: {async: "true"} 27 | ) 28 | 29 | http.errback do 30 | handle_error(http, upload_callback) 31 | end 32 | 33 | http.callback do 34 | handle_http_response(http, upload_callback) 35 | end 36 | end 37 | 38 | private 39 | 40 | def handle_http_response(http, upload_callback) 41 | http_status = http.response_header.status 42 | 43 | if http_status < 300 44 | upload_callback.call(http, nil) 45 | else 46 | handle_error(http, upload_callback) 47 | end 48 | end 49 | 50 | def handle_error(http, upload_callback) 51 | if http.error 52 | error = UploadError.new("Upload failed - status #{http.response_header.status}; error: #{http.error}") 53 | else 54 | error = UploadError.new("Upload failed - status #{http.response_header.status}") 55 | end 56 | 57 | open_connection_count = EM.connection_count # https://github.com/igrigorik/em-http-request/issues/190 says to check connection_count 58 | logger.warn("em-upload.error", 59 | destination: URICleaner.clean(@destination), 60 | connection_count: open_connection_count, 61 | message: error.message, 62 | http_error: http.error, 63 | http_status: http.response_header.status, 64 | http_response: http.response) 65 | 66 | upload_callback.call(http, error) 67 | end 68 | end 69 | 70 | -------------------------------------------------------------------------------- /lib/dea/utils/uri_cleaner.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "uri" 4 | 5 | class URICleaner 6 | 7 | def self.clean(uri) 8 | uri.kind_of?(Array) ? clean_array(uri) : clean_uri(uri) 9 | end 10 | 11 | private 12 | 13 | def self.clean_array(uri_list) 14 | uri_list.map { |uri| clean_uri(uri) } 15 | end 16 | 17 | def self.clean_uri(u) 18 | uri = u.is_a?(URI) ? u.dup : URI.parse(u) 19 | uri.password = nil if uri.password 20 | uri.user = nil if uri.user 21 | uri.to_s 22 | rescue => e 23 | "<>" 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /lib/dea/version.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | module Dea 4 | VERSION = "0.0.1" 5 | end 6 | -------------------------------------------------------------------------------- /lib/tasks/dir_server.rb: -------------------------------------------------------------------------------- 1 | desc "Install/run directory server" 2 | namespace :dir_server do 3 | 4 | desc "Run log_server/n directory server" 5 | task :run => [:install] do 6 | system "go/bin/runner -conf config/dea.yml" 7 | end 8 | 9 | desc "Install directory server" 10 | task :install do 11 | result = nil 12 | Dir.chdir("go") do 13 | result = system "GOPATH=$PWD PATH=$PATH:/usr/local/go/bin go install runner" 14 | end 15 | 16 | raise "Installation failed" unless result 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /pre_integration.sh: -------------------------------------------------------------------------------- 1 | # start warden 2 | cd /warden/warden 3 | bundle install 4 | rvmsudo bundle exec rake warden:start[config/test_vm.yml] 2>&1 > /tmp/warden.log & 5 | 6 | # start the DEA's dependencies 7 | cd /vagrant 8 | bundle install 9 | foreman start > /tmp/foreman.log & 10 | -------------------------------------------------------------------------------- /spec/bin/file_server.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "fileutils" 4 | require "thin" 5 | require "sinatra/base" 6 | require "pp" 7 | require "zip" 8 | 9 | APPS_DIR = File.expand_path("../../fixtures/apps", __FILE__) 10 | BUILDPACK_CACHE_DIR = File.expand_path("../../fixtures/buildpack_cache", __FILE__) 11 | BUILDPACKS_DIR = File.expand_path("../../fixtures/fake_buildpacks", __FILE__) 12 | STAGED_APPS_DIR = "/tmp/dea" 13 | FileUtils.mkdir_p(STAGED_APPS_DIR) 14 | 15 | class FileServer < Sinatra::Base 16 | get "/unstaged/:name" do |name| 17 | zip_path = "/tmp/fixture-#{name}.zip" 18 | 19 | app_path = if name == "node_buildpack_tests" 20 | File.expand_path("../../../buildpacks/vendor/nodejs", __FILE__) 21 | else 22 | "#{APPS_DIR}/#{name}" 23 | end 24 | 25 | Dir.chdir(app_path) do 26 | system "rm -rf #{zip_path} && zip --quiet -r #{zip_path} ." 27 | end 28 | send_file(zip_path) 29 | end 30 | 31 | post "/staged/:name" do |name| 32 | droplet = params["upload"]["droplet"] 33 | FileUtils.mv(droplet[:tempfile].path, file_path(name)) 34 | 200 35 | end 36 | 37 | get "/staged/:name" do |name| 38 | send_file(file_path(name)) 39 | end 40 | 41 | get "/buildpack_cache" do 42 | tarball = "/tmp/buildpack_cache.tgz" 43 | Dir.chdir(BUILDPACK_CACHE_DIR) do 44 | system "rm -rf #{tarball} && tar -czf #{tarball} ." 45 | end 46 | send_file(tarball) 47 | end 48 | 49 | post "/buildpack_cache" do 50 | droplet = params["upload"]["droplet"] 51 | FileUtils.mv(droplet[:tempfile].path, file_path("buildpack_cache.tgz")) 52 | 200 53 | end 54 | 55 | get "/admin_buildpacks/:name" do |name| 56 | zip_filename = "/tmp/admin_buildpack_#{name}.zip" 57 | FileUtils.rm_f(zip_filename) 58 | zip(zip_filename, File.join(BUILDPACKS_DIR, name)) 59 | send_file(zip_filename) 60 | end 61 | 62 | private 63 | 64 | def file_path(name) 65 | "#{STAGED_APPS_DIR}/#{name}" 66 | end 67 | 68 | def zip(zip_filename, dir) 69 | Dir.chdir(dir) do 70 | system("zip --quiet -r #{zip_filename} .") 71 | end 72 | end 73 | end 74 | 75 | app = Rack::Builder.new do 76 | map "/buildpacks" do 77 | run Rack::Directory.new(BUILDPACKS_DIR) 78 | end 79 | 80 | run FileServer.new 81 | end 82 | 83 | $stdout.sync = true 84 | Rack::Handler::Thin.run(app, :Port => 10197, :Host => "0.0.0.0") # ea in decimal 85 | -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Archiver-Version: Plexus Archiver 3 | Created-By: Apache Maven 4 | Built-By: pivotal 5 | Build-Jdk: 1.6.0_43 6 | 7 | -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/META-INF/maven/org.cloudfoundry.test/simple-spring-app/pom.properties: -------------------------------------------------------------------------------- 1 | #Generated by Maven 2 | #Tue Jun 04 09:52:58 PDT 2013 3 | version=0.1.0.BUILD-SNAPSHOT 4 | groupId=org.cloudfoundry.test 5 | artifactId=simple-spring-app 6 | -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/classes/com/springdeveloper/test/CrashBean.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/classes/com/springdeveloper/test/CrashBean.class -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/classes/com/springdeveloper/test/HomeController.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/classes/com/springdeveloper/test/HomeController.class -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/classes/com/springdeveloper/test/MessageBean.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/classes/com/springdeveloper/test/MessageBean.class -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/classes/empty.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/classes/empty.properties -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/classes/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/classes/system.properties: -------------------------------------------------------------------------------- 1 | spring.autoconfig=false 2 | -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/classes/test.properties: -------------------------------------------------------------------------------- 1 | message=Hello Spring! -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/lib/aopalliance-1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/lib/aopalliance-1.0.jar -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/lib/aspectjrt-1.6.9.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/lib/aspectjrt-1.6.9.jar -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/lib/javax.inject-1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/lib/javax.inject-1.jar -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/lib/jcl-over-slf4j-1.5.10.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/lib/jcl-over-slf4j-1.5.10.jar -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/lib/jstl-1.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/lib/jstl-1.2.jar -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/lib/log4j-1.2.15.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/lib/log4j-1.2.15.jar -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/lib/slf4j-api-1.5.10.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/lib/slf4j-api-1.5.10.jar -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/lib/slf4j-log4j12-1.5.10.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/lib/slf4j-log4j12-1.5.10.jar -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/lib/spring-aop-3.1.0.RELEASE.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/lib/spring-aop-3.1.0.RELEASE.jar -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/lib/spring-asm-3.1.0.RELEASE.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/lib/spring-asm-3.1.0.RELEASE.jar -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/lib/spring-beans-3.1.0.RELEASE.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/lib/spring-beans-3.1.0.RELEASE.jar -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/lib/spring-context-3.1.0.RELEASE.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/lib/spring-context-3.1.0.RELEASE.jar -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/lib/spring-context-support-3.1.0.RELEASE.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/lib/spring-context-support-3.1.0.RELEASE.jar -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/lib/spring-core-3.1.0.RELEASE.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/lib/spring-core-3.1.0.RELEASE.jar -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/lib/spring-expression-3.1.0.RELEASE.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/lib/spring-expression-3.1.0.RELEASE.jar -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/lib/spring-web-3.1.0.RELEASE.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/lib/spring-web-3.1.0.RELEASE.jar -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/lib/spring-webmvc-3.1.0.RELEASE.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/java_with_oome/WEB-INF/lib/spring-webmvc-3.1.0.RELEASE.jar -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/spring/appServlet/servlet-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/spring/root-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/views/home.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 2 | <%@ page session="false" %> 3 | 4 | 5 | Home 6 | 7 | 8 |

Hello world!

9 | 10 |

The time on the server is ${serverTime} and the message is: ${message}.

11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /spec/fixtures/apps/java_with_oome/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | contextConfigLocation 9 | /WEB-INF/spring/root-context.xml 10 | 11 | 12 | 13 | 14 | org.springframework.web.context.ContextLoaderListener 15 | 16 | 17 | 18 | 19 | appServlet 20 | org.springframework.web.servlet.DispatcherServlet 21 | 22 | contextConfigLocation 23 | /WEB-INF/spring/appServlet/servlet-context.xml 24 | 25 | 1 26 | 27 | 28 | 29 | appServlet 30 | / 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /spec/fixtures/apps/node_with_invalid_procfile/Procfile: -------------------------------------------------------------------------------- 1 | - "foo" 2 | - "bar" -------------------------------------------------------------------------------- /spec/fixtures/apps/node_with_invalid_procfile/app.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var url = require('url'); 3 | 4 | HOST = null; 5 | 6 | var host = process.env.VCAP_APP_HOST || 'localhost'; 7 | var port = process.env.VCAP_APP_PORT || 3000 8 | 9 | http.createServer(function (req, res) { 10 | res.writeHead(200, {'Content-Type': 'text/html'}); 11 | res.write('

Hello from the Cloud! '); 12 | res.write('via: ' + host + ':' + port); 13 | res.end('

'); 14 | }).listen(port, null); 15 | 16 | console.log('Server running at http://' + host + ':' + port + '/'); 17 | -------------------------------------------------------------------------------- /spec/fixtures/apps/node_with_invalid_procfile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "node_invalid_procfile", 3 | "version": "0.1.0" 4 | } 5 | -------------------------------------------------------------------------------- /spec/fixtures/apps/node_with_procfile/Procfile: -------------------------------------------------------------------------------- 1 | web: node app.js --from-procfile=true -------------------------------------------------------------------------------- /spec/fixtures/apps/node_with_procfile/app.js: -------------------------------------------------------------------------------- 1 | var host = process.env.VCAP_APP_HOST || "localhost", 2 | port = process.env.VCAP_APP_PORT || 3000; 3 | 4 | require("http").createServer(function (req, res) { 5 | res.writeHead(200, {"Content-Type" : "text/html"}); 6 | res.end("Hello from Cloud!"); 7 | }).listen(port); 8 | -------------------------------------------------------------------------------- /spec/fixtures/apps/node_with_procfile/cloudfoundry.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignoreNodeModules" : true 3 | } 4 | -------------------------------------------------------------------------------- /spec/fixtures/apps/node_with_procfile/npm-shrinkwrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node_native_dependecies", 3 | "version": "0.1.0", 4 | "dependencies": { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/fixtures/apps/node_with_procfile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "node_native_dependecies", 3 | "version": "0.1.0", 4 | "dependencies" : { 5 | }, 6 | "engines": { 7 | "node": "0.10.1", 8 | "npm": "1.2.15" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /spec/fixtures/apps/node_without_procfile/app.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var url = require('url'); 3 | 4 | HOST = null; 5 | 6 | var host = process.env.VCAP_APP_HOST || 'localhost'; 7 | var port = process.env.VCAP_APP_PORT || 3000 8 | 9 | http.createServer(function (req, res) { 10 | res.writeHead(200, {'Content-Type': 'text/html'}); 11 | res.write('

Hello from the Cloud! '); 12 | res.write('via: ' + host + ':' + port); 13 | res.end('

'); 14 | }).listen(port, null); 15 | 16 | console.log('Server running at http://' + host + ':' + port + '/'); 17 | -------------------------------------------------------------------------------- /spec/fixtures/apps/node_without_procfile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "node_without_procfile", 3 | "version": "0.1.0" 4 | } 5 | -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/.gitignore: -------------------------------------------------------------------------------- 1 | .bundle 2 | db/*.sqlite3 3 | log/*.log 4 | tmp/**/* 5 | -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | gem 'rails', '~> 3.0.5' 3 | gem 'mysql2' 4 | gem 'thin' 5 | gem 'json' 6 | gem 'redis' 7 | gem 'mongo' 8 | gem 'mongo_mapper' 9 | gem 'carrot' 10 | gem 'pg' -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | require 'rake' 6 | 7 | Rails3::Application.load_tasks -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery 3 | end -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/app/helpers/root_helper.rb: -------------------------------------------------------------------------------- 1 | module RootHelper 2 | end -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/app/models/data_value.rb: -------------------------------------------------------------------------------- 1 | class DataValue < ActiveRecord::Base 2 | end -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/app/models/mongo_data_value.rb: -------------------------------------------------------------------------------- 1 | class MongoDataValue 2 | include MongoMapper::Document 3 | key :name, :type => String 4 | key :email, :type => String 5 | end -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Rails3 5 | 6 | 7 | 8 | <%= yield %> 9 | 10 | 11 | -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/app/views/root/index.html.erb: -------------------------------------------------------------------------------- 1 | <%- host = ENV['VCAP_APP_HOST'] %> 2 | <%- port = ENV['VCAP_APP_PORT'] %> 3 |

4 | Hello from VCAP! via: <%=host%>:<%=port%> 5 |

-------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Rails3::Application -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | # If you have a Gemfile, require the gems listed there, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(:default, Rails.env) if defined?(Bundler) 8 | 9 | module Rails3 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | 15 | # Custom directories with classes and modules you want to be autoloadable. 16 | # config.autoload_paths += %W(#{config.root}/extras) 17 | 18 | # Only load the plugins named here, in the order given (default is alphabetical). 19 | # :all can be used as a placeholder for all plugins not explicitly named. 20 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] 21 | 22 | # Activate observers that should always be running. 23 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer 24 | 25 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 26 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 27 | # config.time_zone = 'Central Time (US & Canada)' 28 | 29 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 30 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 31 | # config.i18n.default_locale = :de 32 | 33 | # JavaScript files you want as :defaults (application.js is always included). 34 | config.action_view.javascript_expansions[:defaults] = %w() 35 | 36 | # Configure the default encoding used in templates for Ruby 1.9. 37 | config.encoding = "utf-8" 38 | 39 | # Configure sensitive parameters which will be filtered from the log file. 40 | config.filter_parameters += [:password] 41 | end 42 | end -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | 3 | # Set up gems listed in the Gemfile. 4 | gemfile = File.expand_path('../../Gemfile', __FILE__) 5 | begin 6 | ENV['BUNDLE_GEMFILE'] = gemfile 7 | require 'bundler' 8 | Bundler.setup 9 | rescue Bundler::GemNotFound => e 10 | STDERR.puts e.message 11 | STDERR.puts "Try running `bundle install`." 12 | exit! 13 | end if File.exist?(gemfile) -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/config/database.yml: -------------------------------------------------------------------------------- 1 | test: 2 | adapter: sqlite3 3 | database: db/test.sqlite3 4 | encoding: utf8 5 | development: 6 | adapter: sqlite3 7 | database: db/dev.sqlite3 8 | encoding: utf8 9 | production: 10 | adapter: sqlite3 11 | database: db/prod.sqlite3 12 | encoding: utf8 -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the rails application 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the rails application 5 | Rails3::Application.initialize! -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails3::Application.configure do 2 | # Settings specified here will take precedence over those in config/environment.rb 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the webserver when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Log error messages when you accidentally call methods on nil. 10 | config.whiny_nils = true 11 | 12 | # Show full error reports and disable caching 13 | config.consider_all_requests_local = true 14 | config.action_view.debug_rjs = true 15 | config.action_controller.perform_caching = false 16 | 17 | # Don't care if the mailer can't send 18 | config.action_mailer.raise_delivery_errors = false 19 | 20 | # Print deprecation notices to the Rails logger 21 | config.active_support.deprecation = :log 22 | 23 | # Only use best-standards-support built into browsers 24 | config.action_dispatch.best_standards_support = :builtin 25 | end -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails3::Application.configure do 2 | # Settings specified here will take precedence over those in config/environment.rb 3 | 4 | # The production environment is meant for finished, "live" apps. 5 | # Code is not reloaded between requests 6 | config.cache_classes = true 7 | 8 | # Full error reports are disabled and caching is turned on 9 | config.consider_all_requests_local = false 10 | config.action_controller.perform_caching = true 11 | 12 | # Specifies the header that your server uses for sending files 13 | config.action_dispatch.x_sendfile_header = "X-Sendfile" 14 | 15 | # For nginx: 16 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' 17 | 18 | # If you have no front-end server that supports something like X-Sendfile, 19 | # just comment this out and Rails will serve the files 20 | 21 | # See everything in the log (default is :info) 22 | # config.log_level = :debug 23 | 24 | # Use a different logger for distributed setups 25 | # config.logger = SyslogLogger.new 26 | 27 | # Use a different cache store in production 28 | # config.cache_store = :mem_cache_store 29 | 30 | # Disable Rails's static asset server 31 | # In production, Apache or nginx will already do this 32 | config.serve_static_assets = false 33 | 34 | # Enable serving of images, stylesheets, and javascripts from an asset server 35 | # config.action_controller.asset_host = "http://assets.example.com" 36 | 37 | # Disable delivery errors, bad email addresses will be ignored 38 | # config.action_mailer.raise_delivery_errors = false 39 | 40 | # Enable threaded mode 41 | # config.threadsafe! 42 | 43 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 44 | # the I18n.default_locale when a translation can not be found) 45 | config.i18n.fallbacks = true 46 | 47 | # Send deprecation notices to registered listeners 48 | config.active_support.deprecation = :notify 49 | end -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails3::Application.configure do 2 | # Settings specified here will take precedence over those in config/environment.rb 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Log error messages when you accidentally call methods on nil. 11 | config.whiny_nils = true 12 | 13 | # Show full error reports and disable caching 14 | config.consider_all_requests_local = true 15 | config.action_controller.perform_caching = false 16 | 17 | # Raise exceptions instead of rendering exception templates 18 | config.action_dispatch.show_exceptions = false 19 | 20 | # Disable request forgery protection in test environment 21 | config.action_controller.allow_forgery_protection = false 22 | 23 | # Tell Action Mailer not to deliver emails to the real world. 24 | # The :test delivery method accumulates sent emails in the 25 | # ActionMailer::Base.deliveries array. 26 | config.action_mailer.delivery_method = :test 27 | 28 | # Use SQL instead of Active Record's schema dumper when creating the test database. 29 | # This is necessary if your schema can't be completely dumped by the schema dumper, 30 | # like if you have constraints or database-specific column types 31 | # config.active_record.schema_format = :sql 32 | 33 | # Print deprecation notices to the stderr 34 | config.active_support.deprecation = :stderr 35 | end -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format 4 | # (all these examples are active by default): 5 | # ActiveSupport::Inflector.inflections do |inflect| 6 | # inflect.plural /^(ox)$/i, '\1en' 7 | # inflect.singular /^(ox)en/i, '\1' 8 | # inflect.irregular 'person', 'people' 9 | # inflect.uncountable %w( fish sheep ) 10 | # end -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | # Mime::Type.register_alias "text/html", :iphone -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/config/initializers/mongo.rb: -------------------------------------------------------------------------------- 1 | if ENV['VCAP_SERVICES'] 2 | services = JSON.parse(ENV['VCAP_SERVICES']) 3 | if services 4 | mongodb_service = nil 5 | services.each do |k, v| 6 | v.each do |s| 7 | if k.split('-')[0].downcase == 'mongodb' 8 | mongodb_service = s["credentials"] 9 | end 10 | end 11 | end 12 | if mongodb_service 13 | MongoMapper.connection = Mongo::Connection.new(mongodb_service['hostname'], mongodb_service['port']) 14 | MongoMapper.database = mongodb_service['db'] 15 | MongoMapper.database.authenticate(mongodb_service['username'], mongodb_service['password']) 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/config/initializers/redis.rb: -------------------------------------------------------------------------------- 1 | if ENV['VCAP_SERVICES'] 2 | services = JSON.parse(ENV['VCAP_SERVICES']) 3 | if services 4 | redis_service = nil 5 | services.each do |k, v| 6 | v.each do |s| 7 | if k.split('-')[0].downcase == 'redis' 8 | redis_service = s["credentials"] 9 | end 10 | end 11 | end 12 | if redis_service 13 | $redis = Redis.new({:host => redis_service["hostname"], :port => redis_service["port"], :password => redis_service["password"]}) 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | # Make sure the secret is at least 30 characters and all random, 6 | # no regular words or you'll be exposed to dictionary attacks. 7 | Rails3::Application.config.secret_token = '95428acc913528bb7b924c0fb5fbb889581eead5e5221732a3115662ce352f7c05d57b52b44fb9518af07e45ddefe0bc5c218cb83958d720e9966ed86f74c36c' -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails3::Application.config.session_store :cookie_store, :key => '_rails3_session' 4 | 5 | # Use the database for sessions instead of the cookie-based default, 6 | # which shouldn't be used to store highly confidential information 7 | # (create the session table with "rake db:sessions:create") 8 | # Rails3::Application.config.session_store :active_record_store -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Sample localization file for English. Add more files in this directory for other locales. 2 | # See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. 3 | 4 | en: 5 | hello: "Hello world" -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails3::Application.routes.draw do 2 | root :to => "service#hello" 3 | match '/crash' => 'service#crash' 4 | match '/env' => 'service#env' 5 | match '/service/:service/:key' => 'service#service', :via => [:get, :post] 6 | # The priority is based upon order of creation: 7 | # first created -> highest priority. 8 | 9 | # Sample of regular route: 10 | # match 'products/:id' => 'catalog#view' 11 | # Keep in mind you can assign values other than :controller and :action 12 | 13 | # Sample of named route: 14 | # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase 15 | # This route can be invoked with purchase_url(:id => product.id) 16 | 17 | # Sample resource route (maps HTTP verbs to controller actions automatically): 18 | # resources :products 19 | 20 | # Sample resource route with options: 21 | # resources :products do 22 | # member do 23 | # get 'short' 24 | # post 'toggle' 25 | # end 26 | # 27 | # collection do 28 | # get 'sold' 29 | # end 30 | # end 31 | 32 | # Sample resource route with sub-resources: 33 | # resources :products do 34 | # resources :comments, :sales 35 | # resource :seller 36 | # end 37 | 38 | # Sample resource route with more complex sub-resources 39 | # resources :products do 40 | # resources :comments 41 | # resources :sales do 42 | # get 'recent', :on => :collection 43 | # end 44 | # end 45 | 46 | # Sample resource route within a namespace: 47 | # namespace :admin do 48 | # # Directs /admin/products/* to Admin::ProductsController 49 | # # (app/controllers/admin/products_controller.rb) 50 | # resources :products 51 | # end 52 | 53 | # You can have the root of your site routed with "root" 54 | # just remember to delete public/index.html. 55 | # root :to => "welcome#index" 56 | 57 | # See how all your routes lay out with "rake routes" 58 | 59 | # This is a legacy wild controller route that's not recommended for RESTful applications. 60 | # Note: This route will make all actions in every controller accessible via GET requests. 61 | # match ':controller(/:action(/:id(.:format)))' 62 | end -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/db/migrate/20110513200739_create_data_values.rb: -------------------------------------------------------------------------------- 1 | class CreateDataValues < ActiveRecord::Migration 2 | def self.up 3 | create_table :data_values do |t| 4 | t.string :key 5 | t.string :data_value 6 | t.timestamps 7 | end 8 | end 9 | 10 | def self.down 11 | drop_table :data_values 12 | end 13 | end -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended to check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(:version => 20101108182500) do 14 | 15 | create_table "data_values", :force => true do |t| 16 | t.string "key" 17 | t.string "data_value" 18 | t.datetime "created_at" 19 | t.datetime "updated_at" 20 | end 21 | 22 | end -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }]) 7 | # Mayor.create(:name => 'Daley', :city => cities.first) -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/lib/tasks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/lib/tasks/.gitkeep -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: test-app 4 | memory: 256M 5 | instances: 1 6 | url: test-app.cfapps.io 7 | path: . 8 | services: 9 | cleardb-b50dd: 10 | label: cleardb 11 | provider: cleardb 12 | version: n/a 13 | plan: amp 14 | command: ruby -e 'while(true); sleep 10; end' 15 | -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The page you were looking for doesn't exist.

23 |

You may have mistyped the address or the page may have moved.

24 |
25 | 26 | -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The change you wanted was rejected.

23 |

Maybe you tried to change something you didn't have access to.

24 |
25 | 26 | -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

We're sorry, but something went wrong.

23 |

We've been notified about this issue and we'll take a look at it shortly.

24 |
25 | 26 | -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/public/favicon.ico -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/public/images/rails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/public/images/rails.png -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/public/javascripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/public/javascripts/.gitkeep -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/public/javascripts/application.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/public/javascripts/application.js -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-Agent: * 5 | # Disallow: / -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/public/stylesheets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/public/stylesheets/.gitkeep -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 3 | 4 | APP_PATH = File.expand_path('../../config/application', __FILE__) 5 | require File.expand_path('../../config/boot', __FILE__) 6 | require 'rails/commands' -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/test/functional/root_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class RootControllerTest < ActionController::TestCase 4 | test "should get index" do 5 | get :index 6 | assert_response :success 7 | end 8 | 9 | test "should save a widget" do 10 | get :make_widget, :name => 'my widget' 11 | assert_response :success 12 | assert_equal 'Saved my widget', response.body 13 | end 14 | end -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/test/performance/browsing_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'rails/performance_test_help' 3 | 4 | # Profiling results for each test method are written to tmp/performance. 5 | class BrowsingTest < ActionDispatch::PerformanceTest 6 | def test_homepage 7 | get '/' 8 | end 9 | end -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV["RAILS_ENV"] = "test" 2 | require File.expand_path('../../config/environment', __FILE__) 3 | require 'rails/test_help' 4 | 5 | class ActiveSupport::TestCase 6 | # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order. 7 | # 8 | # Note: You'll currently still have to declare fixtures explicitly in integration tests 9 | # -- they do not yet inherit this setting 10 | fixtures :all 11 | 12 | # Add more helper methods to be used by all tests here... 13 | end -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/test/unit/helpers/root_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class RootHelperTest < ActionView::TestCase 4 | end -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/test/unit/widget_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class WidgetTest < ActiveSupport::TestCase 4 | def new_widget 5 | Widget.new :name => "test widget" 6 | end 7 | 8 | test "Widget creation" do 9 | assert new_widget.save, "expected a new widget to be valid" 10 | assert_equal 1, Widget.count 11 | end 12 | end -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/abstract-1.0.0.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/abstract-1.0.0.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/actionmailer-3.0.6.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/actionmailer-3.0.6.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/actionpack-3.0.6.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/actionpack-3.0.6.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/activemodel-3.0.6.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/activemodel-3.0.6.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/activerecord-3.0.6.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/activerecord-3.0.6.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/activeresource-3.0.6.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/activeresource-3.0.6.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/activesupport-3.0.6.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/activesupport-3.0.6.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/arel-2.0.9.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/arel-2.0.9.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/bson-1.2.4.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/bson-1.2.4.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/builder-2.1.2.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/builder-2.1.2.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/carrot-0.8.1.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/carrot-0.8.1.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/daemons-1.1.2.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/daemons-1.1.2.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/erubis-2.6.6.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/erubis-2.6.6.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/eventmachine-0.12.10.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/eventmachine-0.12.10.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/i18n-0.5.0.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/i18n-0.5.0.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/json-1.5.1.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/json-1.5.1.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/mail-2.2.15.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/mail-2.2.15.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/mime-types-1.16.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/mime-types-1.16.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/mongo-1.2.4.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/mongo-1.2.4.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/mongo_mapper-0.9.0.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/mongo_mapper-0.9.0.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/mysql2-0.3.11.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/mysql2-0.3.11.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/pg-0.11.0.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/pg-0.11.0.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/plucky-0.3.7.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/plucky-0.3.7.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/polyglot-0.3.1.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/polyglot-0.3.1.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/rack-1.2.2.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/rack-1.2.2.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/rack-mount-0.6.14.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/rack-mount-0.6.14.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/rack-test-0.5.7.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/rack-test-0.5.7.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/rails-3.0.6.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/rails-3.0.6.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/railties-3.0.6.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/railties-3.0.6.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/rake-0.8.7.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/rake-0.8.7.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/redis-2.2.0.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/redis-2.2.0.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/thin-1.2.11.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/thin-1.2.11.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/thor-0.14.6.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/thor-0.14.6.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/treetop-1.4.9.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/treetop-1.4.9.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/rails3_with_db/vendor/cache/tzinfo-0.3.26.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/apps/rails3_with_db/vendor/cache/tzinfo-0.3.26.gem -------------------------------------------------------------------------------- /spec/fixtures/apps/sinatra/.profile.d/set_envs.sh: -------------------------------------------------------------------------------- 1 | export VERIFYING_VARIABLE_DECLARATION_ORDER=SECOND 2 | -------------------------------------------------------------------------------- /spec/fixtures/apps/sinatra/Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gem "sinatra" -------------------------------------------------------------------------------- /spec/fixtures/apps/sinatra/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | rack (1.5.1) 5 | rack-protection (1.3.2) 6 | rack 7 | sinatra (1.3.4) 8 | rack (~> 1.4) 9 | rack-protection (~> 1.3) 10 | tilt (~> 1.3, >= 1.3.3) 11 | tilt (1.3.3) 12 | 13 | PLATFORMS 14 | ruby 15 | 16 | DEPENDENCIES 17 | sinatra 18 | -------------------------------------------------------------------------------- /spec/fixtures/apps/sinatra/app.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'sinatra' 3 | 4 | get '/' do 5 | "Hello!" 6 | end 7 | -------------------------------------------------------------------------------- /spec/fixtures/buildpack.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-attic/dea_ng/65b5415770a767dd38e18eeef7576b9c38aa3509/spec/fixtures/buildpack.zip -------------------------------------------------------------------------------- /spec/fixtures/buildpack_cache/cached_file: -------------------------------------------------------------------------------- 1 | File contents -------------------------------------------------------------------------------- /spec/fixtures/certs/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFATCCAumgAwIBAgIBATANBgkqhkiG9w0BAQsFADAQMQ4wDAYDVQQDEwVkZWFD 3 | QTAeFw0xNjA0MDUxODUzMTVaFw0yNjA0MDUxODUzMThaMBAxDjAMBgNVBAMTBWRl 4 | YUNBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsGes4rOdeRrcNKjg 5 | eHwFjPRonWBfKm+fGXoC7gVEG+CDQbmFSm8sOOuoZcODbnURtQLclbm1DR0BiLAO 6 | CUIWcJ1PLyfJvSn7AXSs/OMZDhLgbIspGZsMISc5g7CcIfYAAevF4RktpsElE3bO 7 | AQ9u5IVIn66CuM4oYi0Ylx6cuBPhV6rkIJFay5Hw/9jhsg7o7HynqrqdEb8XdhV7 8 | 7xkxAUTXMOCiW83W9Cg0BVVcAANZ1bG99+O9Q41JIJ68y3/BaRkmmXpf3YhQcIXN 9 | DC+OCmugMBPo1hEnrG3sWjJDCQMwctq4CeyVvaVFPjeddvQOXhsHQwD0CqZse9WY 10 | wqhdF9p9Xhp/Nb9+Gvz+k2zN+V8EETuH0E+qeR1jQGUMsKGyEvPaz4Tle4hPu1Ec 11 | YIAaXGPjpJobmX5Ewhof4FixfKFPV1/SgSEwTESRCY1KsLR/BuWMO4ooTz9n7Qk+ 12 | YKWIbinVQIMcp1Q+C9tKkolN8Duq9wHWu9ludGMbsRk14hBjfPWeB+1rTFIx5HEc 13 | iiMujCeZvMVcU7tSVpd1EXgrmd6Z2IHgWlphPz9Vi5bO7PxpsU/XH72ht7LD8hQ+ 14 | dZlXkkSVhZbq14VwPS4BrLvi7V1hfDuQm+GLBj8MPlzRnnFQ4LYbfE0EZXeixKbh 15 | XfFG9BUr/PD/lKHpbOJawOVHLhMCAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgEGMBIG 16 | A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFGMR2BBhUyAF3Q1YmJTDAg9kb9Bn 17 | MB8GA1UdIwQYMBaAFGMR2BBhUyAF3Q1YmJTDAg9kb9BnMA0GCSqGSIb3DQEBCwUA 18 | A4ICAQAOWTqB2jDhzu+EulF6AF/DFWIWROwzw9ObtHj6pK3glIyqVt0o8wvQzv1k 19 | +eZN+zkPGzNJFeQBYV9KLGrQ3DkDUgV4o6uYJ6DCKYLctC7t9Hi6RBBrDb2IC3Ig 20 | OCex4k35EN39RQlf5iqz2ZpA1EETJ+TS8y4y+dcysyXYRm1FEwiXiyDygFIHQb7M 21 | 9SdAuXFf7Lv1cqLs+ezIrkU1qSLgJVFns6CKjwTN4czhMjbpjl64rzbHnlbLSgqO 22 | n/5u0H8Bvh5YozJOKsyRRtfWa5OKoMShfZzz5Jw3oNuVc0OSzaXMzSYRUliasv70 23 | pbkRXXBqaQdhMOeLEnEnFzC40SFh2IypV7uDjkjvsEjeejmUvKdsX6/bdtBv7UpM 24 | wDlqiBcm9NzPLtQeMvolNQq3IJbh49O/5G9f3uUH4wINNiiVcaA0SqZsNC1pi5ij 25 | EefJk9hGMIhgAp6WJSMlawZ94kBVzW4Qkdo624I4xsjhJx7mt3p4VbpIh9cA3gC2 26 | NDlxA0cYlEX02jEstBGYSI1eNRFEdZb+LLCT5WvGwN0Fm8TSfLbEBkdnWTxf0oiH 27 | udwBb5eUvh9Kl1wxc2M8RdGLQVYoIS2oqINmm6RTLSb8NnTGmHIAUXsTTSCfD2Pq 28 | vMxgH0aK3leWQWVeA4/gtsfnIC404kDNe3ydBDt3j7GTjQ6uZQ== 29 | -----END CERTIFICATE----- 30 | -------------------------------------------------------------------------------- /spec/fixtures/certs/client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA2EYqjSYT5ElD6uTIK4RYV6MHq1KHWZcJXmivY1RKyQTKNGVG 3 | BCz64nGoOYEOvDwBHG78jPLQ7BDBq54KP3iwZV0nKAsNu7aMj9YqmwzBJa5BKSfX 4 | fqe9ijnIa128dTcmADibs5E5Ns7Bpl0Yzo7dVOfY5SWVaawH/BKWNy9odb0wA4mC 5 | i89m8bjww27fGB1PrujQYvazCYkzjruh0m2+qLIOi6xk+r821GI2tg+43SGi+rlg 6 | kXVMMB11Aj25SxSATdu9qxxoIR48yUUiXlNUiWXYeXYJpKzOtZAHoEULcduw7pKE 7 | 6tr6FkkHOkYuxkw0f6lNOLKsi5Lx0kg7SRumWwIDAQABAoIBAGPkVLcJBVsFtAcE 8 | UKg44ozn0h0Ba16n8j3W4FtijYA3ZCW7ORGorOB2WprkzyeGF4VIhgGcDX9bZfic 9 | cGNMnTzUP8exA1DWSioNY3Rl+bgruhEGQ/ROCa+UbMuaafdB1512iAPyaZi0+qCa 10 | k2Ckf/o9c+Ky70X1nWKCOzuV/AuU0wD6rgQ1JZ7QSdjn/WkRHwQIfLq4NNnliq4n 11 | Fw5RtPw90f2ZGu16zl2WV3S5/c5HXsVa2OZEIWfZ4w6ZfJe8LvIsD6vnCOsgrhwG 12 | QvoesVWJmLma/hzVgekQqf844LEq1C9OmyJZwn872HrUiXr0sNVkmcvmtw/E8oTn 13 | Y+MzXVECgYEA7G8/IQiFumLLi6nG1G2VelLa14B9Kd+Q69B3UcCaRAqbPugEFCUD 14 | YXDP7ODzwsAYuqo65OuvGVgwFATi0q8vdTTsPiqniWrVXXN/irAuE7vGP3fuHEHv 15 | lLBtlsPHtft135wJCuYWunFzSVGKaphXkBt5D0i0kPnnVA/R3Bu42HUCgYEA6ivU 16 | ZPBgRfBSSLDWyl2MaqL7I9ZOK7pMIxvw4WueTk1ZhQjdPJEDOw7s9TeoS8QODriS 17 | Y56gqxJJva06D0/KQxTIcaYvyNLJ8/KiGPiSY8Nu3W5/BAkc3xmhRfNIaMdFCv/e 18 | lhr8Dx/4GPQtCiio86sqjHM14iPyQ/H5zqNwKY8CgYBvc31Rp//JvXpviodkaYe1 19 | 0srAHRxjGbTpxCoKynytqEZIH/yUNmJik1ggNGYTnpPJoicC9CplCxIWzbrST6xS 20 | VrEqGNdCyq5rT9+GU8hUmwY3suP2RAqYj5YJJJ7GpROKZbZJge6i0onivOI/nzJf 21 | huPJFZ+wW2z1TkPBuvumZQKBgQCknttZeoKzqBawc2EMMmecL6dNskKSCw2rQOE+ 22 | F2HauhXMqEafZXJX+k4qtU3itZkMUETZM5LPsLOWXEkMqpMORXYEL0g4bbb7f/7k 23 | fxqqd6FgmWVBeQaK8IGnozesMSogaXAZJi88ksTfBgkDs0HSyunGgtfGem69xUZQ 24 | NiWLkQKBgQC8iwuA4RPwqxqdTJqHD5nvA6Jm6LrL9l/hEaSJmYQWdYP84FIy4MhO 25 | G7TKC4+QBsFjvpOHZ7XqXBuRkDKcjEFwEsgfuhTl/XzFv3osuAD8/hhSyQWvnnPa 26 | O5+Dm2jTrEMpSc0ET3zSkSlmGVYoPqEGy5khtp3pNHO/zd2+TyZtyw== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /spec/fixtures/certs/hm9000_ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFBzCCAu+gAwIBAgIBATANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDEwhobTkw 3 | MDBDQTAeFw0xNjAzMjIxNzQ5NDZaFw0yNjAzMjIxNzQ5NDZaMBMxETAPBgNVBAMT 4 | CGhtOTAwMENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA95++8GzO 5 | YBerdJ/YJrDLzY+TplCbzpcYcw/e0kCd5/ck17oGSuZ6wGlOAbeypeyDmKkczvi8 6 | NLvZyurQM6mvK8QZAQtwVpOmsS8cIJzqfB5+VOZdSChBzyB/4la4Cyka4IbwWm2I 7 | XJzl6hYfpedqSL8emcyIA++CiXjCIf5zs4KYkPULtPwD3MbKLe4fg7EK8uNINMaJ 8 | um5ZrMipqx0Ci47AZSAorkbxdXLbOsa1Y2Rwouako3zJZnSfonYHsXFEtthpny8N 9 | 7xWAsCOkp0E9c2AnuPy+jLMrEV/hAaovhaPtSahW63E0fXQ93fSp1SjyPmcuE7hx 10 | uejUyLaWst1GfzAmLhNkgvPZN/0H7WOwTxRVxJ80G3tFXuTRiTX72OLY7RLB58bw 11 | Js3wVww4sjiEzICK/8o6OlOdCHeGMtqCTaX4EM3VmUME7S4+//yvC7lR7w3wT1vn 12 | b6UzHhWzLBOaqt10FrozhZzJIchjonpPWsaiapjJWXUMmLKdSq8CkzrArUKd7SIG 13 | 6IyQrMCC7Mu/86l9X+lkcHCugK/UO1l0bhgVK4LyV7MTFopoi3fxvLmtJw19BZZD 14 | fJ5pLxVFOLtHlsLukRTvw06GVVgqnllCpgyVt4XnZaBHES+uYpEU1bNl/wlAecry 15 | HG792BiDctoUV5VznKZ+oCter124BIdxwwECAwEAAaNmMGQwDgYDVR0PAQH/BAQD 16 | AgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFEeLoLApjFGOX77sBfAT 17 | makJ8ezYMB8GA1UdIwQYMBaAFEeLoLApjFGOX77sBfATmakJ8ezYMA0GCSqGSIb3 18 | DQEBCwUAA4ICAQBaOaiKHYPPp0hIlbG46lAv7b1LwcwOX26JEYIzq4tn19YtLBxj 19 | aI+ismRd6yT84TRJlCjIoAi7AdDfsW7UUNq5RkkWh0yBTL/uOHSoshelhXibYWIc 20 | SiwG9zpKqFUr2UT3MfstXnNaSleAbzTeGC28YcX4pTFaLtzp7SvybIf4fZgoTI0b 21 | mnJM5RNzsAxow1kgQE+fxIZj6DqvPxHhWa4aqWElbEBsjbo5uPVjrPxJmoehu2mt 22 | HpVmaU3yRVsfypoGV0kvLhuUoLeCIqEvwV/jGIoDiYtZULoJEAHGFgIvtbK2yfce 23 | /i2W7r8WTGbKTJobDN3wPO7oR89LovFMPB1BKaro73w/HZyMWew4liMG7wz1OG18 24 | chBI9F5Gb9r0lzdLFzDmS7WxDpIHS0Rht3PJo/ZgfPzybaM7sX7ECpk+pAja0Y9n 25 | pKbcJzqjLSKVypKQ9mC1thBw6AzgwcJ6FwcIyY5eD3EnYYGLgCPAxjEd+rtb9/RM 26 | Fj4+H6sR4e+CC5vrvZ3hKdtExeDwN/ikrSKvXe9heHwZja/NGUaYCxtf9A8gKZTv 27 | Tj2RtnbuAU2XEM5mAfU63aX/3tL8tuRE5kDBhdM5RZtk7Q66dnqbUwDPCfY9s3c3 28 | hrvXqOOGlow/qLVOjphLitSQlnHf7hnIYIDUg3cP7GAbTrAnz9XAAMS/Kw== 29 | -----END CERTIFICATE----- 30 | -------------------------------------------------------------------------------- /spec/fixtures/certs/hm9000_client.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEJjCCAg6gAwIBAgIQYmSfPw1CR4iBjVCtYDqBVDANBgkqhkiG9w0BAQsFADAT 3 | MREwDwYDVQQDEwhobTkwMDBDQTAeFw0xNjAzMjIxNzQ5NDdaFw0xODAzMjIxNzQ5 4 | NDdaMBgxFjAUBgNVBAMTDWhtOTAwMCBjbGllbnQwggEiMA0GCSqGSIb3DQEBAQUA 5 | A4IBDwAwggEKAoIBAQDqQ+H3Pwa1kgNW8XUryyjN9hSp2gLbp3HgV+u/K57fIYQ6 6 | 7V4ciCDoc+j998rZXRJU3tlNEzk1+n9SRZXId4AeVFeXZr+R74uO9hkSRfdcMlMU 7 | Io8A7ITpAMP2+qb+2Rss0biPolRSLBN7kGWA9x2KHFmoDURKV0gGbTCKcPQk7nta 8 | z0Qnrk369fVa6nXRyz/JOgFcLYwXncnMDtP0QIPo2P3NLInjIljP4+f+0P/IUQL+ 9 | 1AldU7jooChKwiODkCsjHwakv5aK1jGgs2Yo3TYGWyS2Qv/v6jIwAelhm372JHSW 10 | lke3n6LKEDxJWXumMRq7zhzl9/H7oy97ULytXuLpAgMBAAGjcTBvMA4GA1UdDwEB 11 | /wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYE 12 | FMmYyMcuvJkFRExvHL9QbvjY096XMB8GA1UdIwQYMBaAFEeLoLApjFGOX77sBfAT 13 | makJ8ezYMA0GCSqGSIb3DQEBCwUAA4ICAQB5nJ4olbBigXvxJIkaq8A2f8tZnQPD 14 | 05g31GcT0YJrbX2qdFPJR/TpCVAmJyamtZUR7DPKFso/TliHi91SM1UsvkQlInkh 15 | XDSXPfET/QiKCrUn9oWeFQEoIboQiyAIhdhzZNryGFmXZeER6LMofIZEFLNT3ZHj 16 | NmLStZl3PS2bpofMDCrJjvdIGjeEsOe+Qer896aAJ85/SKvo7ZJS3AyS65zx8vda 17 | 0PcWDkjd+CVw5IJeQNzCbT0wp0CYuFtABc/cSUyYNeiGxS3rhG8oP+lEek9Di4z1 18 | vy6JjgmUCRf0G9ccU7Gtxh+f0apK8NsVxFalTlHfr0h5Ho4VdI+FnxSp1/Na2Qye 19 | 5X2OGPYbYacJR1oAl73MVZSjZFQk13dVOQoeqXxN4AYLfqMS/2WdIHbAsmH10Y1w 20 | 9QFSr4e2T91Kl+oLZ/r7d4rpycWgDaxxcGXXH2mplWtIm3qi0QgTjsLo+mfWWpK5 21 | /55dwGn+46mcNVbSJJO2+EmIrlwga3qpTqyNsoeuNPOw3Yrrtle/Bws7qYfMQCfs 22 | AkPwnrNv7PvzHtQ+BY2v1CNGaxZnQBE0ZcqSy+su8LMXv3jx44e/icIL5rfhT92f 23 | qifo/aXnEk4g4/Bgpy5wsUrsx+ANCtfdM1w5GD7Mf8WHltxj/mNw7lbTzRhy7naz 24 | KVQZo80q6VFHhQ== 25 | -----END CERTIFICATE----- 26 | -------------------------------------------------------------------------------- /spec/fixtures/certs/hm9000_client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA6kPh9z8GtZIDVvF1K8sozfYUqdoC26dx4Ffrvyue3yGEOu1e 3 | HIgg6HPo/ffK2V0SVN7ZTRM5Nfp/UkWVyHeAHlRXl2a/ke+LjvYZEkX3XDJTFCKP 4 | AOyE6QDD9vqm/tkbLNG4j6JUUiwTe5BlgPcdihxZqA1ESldIBm0winD0JO57Ws9E 5 | J65N+vX1Wup10cs/yToBXC2MF53JzA7T9ECD6Nj9zSyJ4yJYz+Pn/tD/yFEC/tQJ 6 | XVO46KAoSsIjg5ArIx8GpL+WitYxoLNmKN02BlsktkL/7+oyMAHpYZt+9iR0lpZH 7 | t5+iyhA8SVl7pjEau84c5ffx+6Mve1C8rV7i6QIDAQABAoIBAQCd8BYwMvW7BC3s 8 | N+3fEKD5+qYDvU+f90v3DE+MG0SI+nfDorSIV96X2U2Dq8O+t9jcD0JBjGxN0Ax2 9 | 0yMgfDCMQiCrmkIdiXp1b8WHZFRYfDU2pfXzkYIp9LIpmONtfcGsfdgIPlDK9ZxM 10 | +hT01aW8r1Bi1E7uL9oHOyY2r+Bp5HRfGhg4sawoY1fcIr77yQsLLWQUzZd/BiNB 11 | ign4I+7FGsWqIlPuQl+4ROL4nxI4jJhSzwBmqcg628UfcMbFmmyHAIvxk1m399gL 12 | VKE/vXLhh4rJAiO5QOgTHzdFpwPNjoVgVwAZ/gtVlmudviOntT/kb/B7JAIOyI38 13 | Dy+QBx+BAoGBAPOYvEErPFQ+xMPEciV4lhvIalzDmZPS6daIemOewB2SvlM7tFp6 14 | sOOi3YqBFrgChmbXdqZ231tM93AXbis6o18xwduukBcCHsmE1pdjE/8LpN8hM/gq 15 | 6D1/Aia5/tYRMWXNLBdHToVH2LEb4VOb8VyQmBw4ha+ErYX9hDJE9HYFAoGBAPYx 16 | gy++4Rk+BqQ35cc/tsr7zXf+N6in6LtF6cB2m9iiKIJmOTRdPGWWH6eHcx05Wldf 17 | FVZaOm67M4rxVeFxYg3M2xaN/b5p+evSXj3CaO1g/vj1uDuSLe+Rwx5bPAIYP0oH 18 | 3xXfnirKYFN8NeludLmd1ugvnzynkTyhjW9jsgqVAoGAQT893W7JP/94EL/Dm0nL 19 | WzG5W5OAxVmPDMmNjrVs2/78P6sUSc/nJXHwSTL+dNcoTM+Vlkzs0rWw6TM9bRLN 20 | w6e+NytwqNkGig+ssuv05TGnPaTVGLnVbvNgDiNvoMMGKjBkiO+uloCCCB5holV4 21 | tsR8GcMB+9tb5zHagOR7XkkCgYEAxJeDB2RoQYzrJsVQIkx5fhuW1oPVFEOIr8rD 22 | PyI3jsTmjkJs2ai+Kuy3OEMMJEEBKBXQZxVD5dps7T2L86tjvltgc+FMx3m+zZY+ 23 | FRkiSrQSxuEIjeFL9ysxYRM/lvifyVEBzmiz172lL+VW86F1aidlTiGBCaFRc9YZ 24 | p+I3cQUCgYB6a8ANHwpGzGdMI1zY7DS+9R03iC/XVs2jwT0S5iQwcxTOEqMxfm3w 25 | Oef30PSOZaZE5BiNSVyEcGeOd2HYDTWtKRrHNtediekiahrYFdTdnktSCkdBjO0g 26 | eCJXOFkpVpQOH3r96T8+0sVryObXUyoTIeKK7CP/8UIHgQ9SuDAr6A== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /spec/fixtures/certs/hm9000_server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpQIBAAKCAQEAxBXmT0iez8og7xPAc+BMxUqDKDLTqoO4JScv+CpSPaj1KtUr 3 | zcaKWxpQaUNE/ogM8xdeWgCwFbOHSwYFgkfWfCAMc231frSjD/5LZwhUNER56j+j 4 | NZHkay1TZmxhRrkVbVkL91zH25u8v6H7yLaULez5J0J3SQ1OWX1aTY0C8irJI0u0 5 | /hl8KyqlCAGBhptr9Y2rB0c39kmtZOtC43vKJvkg+6mnL0D7wiwWGlPGX3SekWTX 6 | MpRlD/NWjuPAeGqvyW8n+Mq690dzTNsoDiw5/wchsQQUvRXUkI7qODg5WfvjQDvz 7 | 3s/iFmOk3WtOd1uK3NgU+v82TE7kEKlteLQi/wIDAQABAoIBAQC2x7sxuH3hL4dX 8 | lE/XNAQb/AMaW5aW4cWrTEYYzEpxE6fFT4RPgnzBw1ulY4FSyjtX9jaLKwz4jarV 9 | sj08Xf5/Idi5WIx4maVOaTqcRlVHi3UheESI0P/dMBkH5osew7RHg7P11tStzxQ4 10 | haQQSrEBxl3RzNEolXCtVn3wwDs4xChBiADDDGPoB+EY/HxOMjv5dPcB8XoS8zY/ 11 | Vfj2UYwFjN3On9bT/MwGELabaGTJUAukjpp3foPlakPeNuExBQitS/j2y8UK2zSM 12 | NK4mh3ozNKXm+PNEDDdt/wHU6n4sli1qOwGM6W+O+jIRBie3kCG2TnCcWyJq8eRa 13 | CkRFnmgBAoGBAMVp1AFkomInWqCzEf2G+NcFOYA2UjdFaZFdchBI/+Xbo2KYxJyH 14 | QP7EqOGpEtKoNIICf80epuNyqiVJKFAdkTIzm2u3DkNvy0uv6+vDaFzJndT+0Pz5 15 | ja/B95D0FGK9Wxefzk2bsr8uMLtudpL0HC7nTF8d7qYpPnUYZ9K4UWj/AoGBAP5H 16 | MMVyXv5fUWylvUQBlerSK3M1SxOHoHM/rzQ+s11c2yXi6KOv41vCiCN0FROF8w9w 17 | dB3oNTvvNdOpgXpUKz2pUZnu3Gy05qfURysUpEOuY3yjL3zrUab21/v6CqHBmHbx 18 | S6pExwtA/1a1jIK7aOP3J354O6fBJ7gUAwWNU0YBAoGAZFBQcJzIfwPsY3hbDe9G 19 | B00aWPJBeOCGsl0/ltLhZdXkAjgKwFdiSZN5FaIqdG0QsXe/pWWL/EvJ9cRk/cDo 20 | hRAATA3q642lSkKMqXLUV0IEN7V8UPAmdUclG71LlpkN7sU/ueQCQm+gR73r9uSg 21 | ZZ0XC8ZT/I88s5fBOix7AdUCgYEA0y0UmCZwNhm7adwL35rhgu+hHkE4ZLPS0WQm 22 | geFA3Vzss/5K7wZ4cBCTN76Ecn5gaoYeKWhcLbZwoaS433tfngostwblEXL7zI5D 23 | vMeb1InUHgL4wWo9mciPf7kdc1Fw6fdju7j7BaGpbb3BQCGc7xaoOiRoht2nuv0D 24 | tWy1qAECgYEAuuyMbHOC0zpLwq2+WBdqOS2eBSKPWKDSZd18mRZ2yQnItKXrcpqC 25 | 14P/GtIHel6J76J6AY4H5zEu7ggHSN2uD/d9cZ95AIGC9JAQdzCBed9c+HVCrQip 26 | y9tP56l7FAwhilInAdgaw9QpHf/cq3a8LW3qEnagBmou8EgXuaES/pU= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /spec/fixtures/certs/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEA18nMoqBMirTVJUzGATXfm9lajQPrCDy7uAq06MsvRyiWT7Zo 3 | NJAs7zg8MFwgHiTj+0v0OTuPAA5OOYudOmV7MNWJSNt5WkDvKCqWR4ydgwT89ycp 4 | WK9Kx/D0r6/mOwhAZ2000oQAFVNXpllhLZaA5WfT9lAPjgTqshg7t/uN+NJ1tIEM 5 | nZo1OE8NDBcot0LA0/wqHvTJo9qN6fZ9dR0iixJ4wj2yhbVsP78xzIJSZCNVk6cF 6 | 2FIjmCMXd/z3Mbi3vnHQ5INE4n7qxtwp8OdsaKGniz6cZCZZKX2iBV4dlN7uhgmj 7 | 9WMMrQ8nu2gt7AyW8TO/2eX3bQGaiiSzmYi0DQIDAQABAoIBAA0e/ytwVdclUcSX 8 | 235dWUBvwfrLHzJ8BuoU9JUnLbPE+wbTz5hKoADXyYkjQOy0T4yLPMlyHqrhM855 9 | 99CCNTBt+5ALkGB1jFVD6almGnnKBtu8bN4wonpv42D/cB9szAW99TVLw3mIfM7n 10 | pmoQObw5SQE0Mw3urJqxsIfqauKTROa2FF9xMGJK3nKX37x5nuZRK2F9/nXJ5FFO 11 | muADrkEgkDvlloRTtMNTuwQAy9Xx3ThpzPCmJeSK49RDFc3ZYfN2qlcykC5ngFEG 12 | qySHmBClalXq0hoxFX1NePz6FK3hT4ihvWrh/UMSJxmCRVyQf7SDEE5gvpwdancw 13 | xvTBZL0CgYEA3oeAxCbbDY5zVqoC/7ohkFh1KkLrlWamv/UBdIBaQuYa5SVp4dy4 14 | 2t5MM6WZSif73AKoqxJa7MpMm9iD20GMIc+tzOvJfUBKDaJxUkHyFgzYOp0Rems0 15 | mwlAMnXWEe5aP9H2JfxBBEcChW4zAL9yysLrXMY7nIL+6EV9sxQmM3cCgYEA+D67 16 | mqigM7W6AK4ei8TU6U6/OspICOPy+ob7UaMTSTRdT528+6SKJikUBdBTXHiwO68/ 17 | 6/48LIuekca60sWiewaPONnEjEbwyVJ6oosg+6KMp6AMoCQJuZqvgkpGekabdhxi 18 | wx7hZawsfIy32czTmNoDd9zM6uMSJCDaxVIpjZsCgYBQRb8cgwu3+y/+DjwgtasP 19 | 3+j8AsHq1enHCwGoXfixc0I2cOWeFToVseT5KH6AcqX2+nqPo4JrMpqqCVOfTyjW 20 | tYzzhAbeCMft3TBVxPYoICAMHMbGS0/hsVeYHEvJkhq6NOo/T5zAh0EQku0REv68 21 | x82RXOz6C4MalJ7Ab79MBwKBgDo0SzxZ6SX2p+Pc72CKB/mj8wZPw1ACUkgXOfnM 22 | KItzcaVnMJY34AROgVlTnRgDXB7CWlR0yZsQKV+4nrkoiY4a3Z3QwVq6j15ErmhK 23 | ArO9SwlKZ2tsx7Ly58++poOo5m5s/WMObH6wPcX+IGU6tyoRg9qotIsmLmy2H1VA 24 | jGmdAoGBAIHsv2gTon3N0yduueS/g8cwiMf3SeJj5CyW+61qrrUc8TabMvrMzykl 25 | gVnMLy8Ly+FgB7FPXBpbVlj3fdW1IvXHRk//0KTcprto6ZoZyA/LqGzkNVhFV0+v 26 | bNXaQPyS7NIpK71vcS+jWGs9tgQnM/dl/U0xWvo1ex3W0T3Ewrco 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /spec/fixtures/fake_buildpacks/5_second_compiling_buildpack/bin/compile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | for i in `seq 1 5` ; do echo 'Compiling app...'; sleep 1; done 4 | 5 | exit 0 6 | -------------------------------------------------------------------------------- /spec/fixtures/fake_buildpacks/5_second_compiling_buildpack/bin/detect: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # bin/detect 3 | 4 | if [ -f $1/package.json ]; then 5 | echo "Node.js" && exit 0 6 | else 7 | echo "no" && exit 1 8 | fi -------------------------------------------------------------------------------- /spec/fixtures/fake_buildpacks/5_second_compiling_buildpack/bin/release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cat < Some admin compilation output" 5 | exit 0 6 | -------------------------------------------------------------------------------- /spec/fixtures/fake_buildpacks/admin_buildpack/bin/detect: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # bin/detect 3 | 4 | echo "Admin buildpack" && exit 0 -------------------------------------------------------------------------------- /spec/fixtures/fake_buildpacks/admin_buildpack/bin/release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cat < Shutdown buildpack output" 4 | echo "-----> Copying application to $1" 5 | 6 | DIRNAME=$(dirname $0) 7 | cp $DIRNAME/run.sh $1 8 | chmod o+x $1/run.sh 9 | 10 | echo "-----> End" 11 | sleep 1 12 | exit 0 13 | -------------------------------------------------------------------------------- /spec/fixtures/fake_buildpacks/graceful_shutdown/bin/detect: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # bin/detect 3 | 4 | if [ -f $1/package.json ]; then 5 | echo "Node.js" && exit 0 6 | else 7 | echo "no" && exit 1 8 | fi -------------------------------------------------------------------------------- /spec/fixtures/fake_buildpacks/graceful_shutdown/bin/release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cat < 3 | 4 | if [ -f $1/package.json ]; then 5 | echo "Node.js" && exit 0 6 | else 7 | echo "no" && exit 1 8 | fi -------------------------------------------------------------------------------- /spec/fixtures/fake_buildpacks/long_compiling_buildpack/bin/release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cat < Some compilation output" 4 | exit 0 5 | -------------------------------------------------------------------------------- /spec/fixtures/fake_buildpacks/no_start_command/bin/detect: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # bin/detect 3 | 4 | if [ -f $1/package.json ]; then 5 | echo "Node.js" && exit 0 6 | else 7 | echo "no" && exit 1 8 | fi -------------------------------------------------------------------------------- /spec/fixtures/fake_buildpacks/no_start_command/bin/release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cat < Some compilation output" 4 | exit 0 5 | -------------------------------------------------------------------------------- /spec/fixtures/fake_buildpacks/ruby/bin/detect: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Ruby/Rails" 4 | exit 0 -------------------------------------------------------------------------------- /spec/fixtures/fake_buildpacks/ruby/bin/release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cat < Start command buildpack output" 5 | if [ -d $2 ]; then 6 | mv $2/* $1 7 | 8 | touch $2/new_cached_file 9 | fi 10 | 11 | env | grep -E 'HTTP_PROXY|VCAP_APPLICATION|MEMORY_LIMIT' 12 | echo "-----> End" 13 | exit 0 14 | -------------------------------------------------------------------------------- /spec/fixtures/fake_buildpacks/start_command/bin/detect: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # bin/detect 3 | 4 | if [ -f $1/package.json ]; then 5 | echo "Node.js" && exit 0 6 | else 7 | echo "no" && exit 1 8 | fi -------------------------------------------------------------------------------- /spec/fixtures/fake_buildpacks/start_command/bin/release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cat < :integration, :requires_warden => true do 5 | let(:dir_server_port) { dea_config['directory_server']['v2_port'] } 6 | it "asks dea to verify instance file paths" do 7 | result = Net::HTTP.get(URI.parse("http://#{dea_host}:#{dir_server_port}/instance_paths/instance-id?path=/file&hmac=×tamp=0")) 8 | expect(result).to include("Invalid HMAC") 9 | end 10 | 11 | it "asks dea to verify staging tasks file paths" do 12 | result = Net::HTTP.get(URI.parse("http://#{dea_host}:#{dir_server_port}/staging_tasks/task-id/file_path?path=/file&hmac=×tamp=0")) 13 | expect(result).to include("Invalid HMAC") 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | SPEC_ROOT = File.dirname(__FILE__) 4 | 5 | $:.unshift(File.expand_path('../buildpacks/lib', SPEC_ROOT)) 6 | 7 | require 'bundler' 8 | Bundler.require 9 | 10 | if ENV["CODECLIMATE_REPO_TOKEN"] 11 | require "codeclimate-test-reporter" 12 | CodeClimate::TestReporter.start 13 | end 14 | 15 | require 'socket' 16 | require 'tempfile' 17 | require 'timecop' 18 | require 'timeout' 19 | require_relative '../buildpacks/lib/buildpack' 20 | require 'webmock/rspec' 21 | require 'rspec/eventually' 22 | require "dea/utils" 23 | 24 | Dir[File.join(SPEC_ROOT, 'support/**/*.rb')].map { |f| require f } 25 | 26 | RSpec.configure do |config| 27 | config.include(Helpers) 28 | config.include(StagingSpecHelpers, type: :buildpack) 29 | config.include(BuildpackHelpers, type: :integration) 30 | config.include(ProcessHelpers, type: :integration) 31 | config.include(DeaHelpers, type: :integration) 32 | config.include(StagingHelpers, type: :integration) 33 | 34 | config.before do 35 | WebMock.allow_net_connect! 36 | 37 | steno_config = { 38 | default_log_level: :all, 39 | codec: Steno::Codec::Json.new, 40 | context: Steno::Context::Null.new 41 | } 42 | 43 | if ENV.has_key?('V') 44 | steno_config[:sinks] = [Steno::Sink::IO.new(STDERR)] 45 | end 46 | 47 | Steno.init(Steno::Config.new(steno_config)) 48 | end 49 | 50 | config.before(:all, type: :integration, requires_warden: true) do 51 | dea_start({ 52 | "intervals" => { 53 | "router_register_in_seconds" => 0.5 54 | } 55 | }) 56 | end 57 | 58 | config.after(:all, type: :integration, requires_warden: true) do 59 | dea_stop 60 | end 61 | 62 | config.before(:all, type: :integration) do 63 | WebMock.disable! 64 | 65 | start_file_server 66 | end 67 | 68 | config.after(:all, type: :integration) do 69 | stop_file_server 70 | WebMock.enable! 71 | end 72 | end 73 | 74 | #Timecop.safe_mode = true 75 | 76 | TEST_TEMP = Dir.mktmpdir 77 | FILE_SERVER_DIR = '/tmp/dea' 78 | 79 | at_exit do 80 | if File.directory?(TEST_TEMP) 81 | FileUtils.rm_r(TEST_TEMP) 82 | end 83 | end 84 | 85 | def by(message) 86 | if block_given? 87 | yield 88 | else 89 | pending message 90 | end 91 | end 92 | 93 | alias and_by by 94 | 95 | def fixture(path) 96 | File.join(SPEC_ROOT, 'fixtures', path) 97 | end 98 | -------------------------------------------------------------------------------- /spec/support/custom_matchers.rb: -------------------------------------------------------------------------------- 1 | # Expects two directories to be identical in content, though not filesystem metadata. 2 | RSpec::Matchers.define :be_recursively_identical_to do |dir| 3 | match do |container| 4 | comparison_cmd = "diff -rq #{container} #{dir} > /dev/null 2> /dev/null" 5 | system(comparison_cmd) 6 | end 7 | 8 | failure_message_for_should do |container| 9 | details = `diff -rq #{container} #{dir}` 10 | "expected target directory #{dir} to be recursively identical to #{container}:\n#{details}" 11 | end 12 | 13 | failure_message_for_should_not do |container| 14 | details = `diff -rq #{container} #{dir}` 15 | "expected target directory #{dir} to differ from #{container}:\n#{details}" 16 | end 17 | end 18 | 19 | # Expects a string to contain the path to a real file that is executable. 20 | RSpec::Matchers.define :be_executable_file do 21 | match do |container| 22 | File.exists?(container) && File.executable?(container) 23 | end 24 | 25 | failure_message_for_should do |container| 26 | "expected target file #{container} to be executable" 27 | end 28 | 29 | failure_message_for_should_not do |container| 30 | "expected target file #{container} to not be executable" 31 | end 32 | end 33 | 34 | -------------------------------------------------------------------------------- /spec/support/em_helpers.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "eventmachine" 4 | 5 | module Helpers 6 | def with_event_machine(options = {}) 7 | raise "no block given" unless block_given? 8 | timeout = options[:timeout] ||= 10 9 | 10 | ::EM.epoll if ::EM.epoll? 11 | 12 | ::EM.run do 13 | quantum = 0.005 14 | ::EM.set_quantum(quantum * 1000) # Lowest possible timer resolution 15 | ::EM.set_heartbeat_interval(quantum) # Timeout connections asap 16 | ::EM.add_timer(timeout) { raise "timeout" } 17 | 18 | yield 19 | end 20 | end 21 | 22 | def done 23 | raise "reactor not running" if !::EM.reactor_running? 24 | 25 | ::EM.next_tick { 26 | # Assert something to show a spec-pass 27 | expect(:done).to eq :done 28 | ::EM.stop_event_loop 29 | } 30 | end 31 | 32 | def after_defers_finish 33 | raise "reactor not running" if !::EM.reactor_running? 34 | 35 | timer = nil 36 | 37 | check = lambda do 38 | if ::EM.defers_finished? 39 | timer.cancel 40 | 41 | yield 42 | end 43 | end 44 | 45 | timer = ::EM::PeriodicTimer.new(0.01, &check) 46 | end 47 | 48 | module HttpServer 49 | attr_writer :blk 50 | 51 | def receive_data(data) 52 | @blk.call(self, data) 53 | @blk = nil 54 | end 55 | end 56 | 57 | def start_http_server(port, &blk) 58 | ::EM.start_server("127.0.0.1", port, HttpServer) do |server| 59 | server.blk = blk 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /spec/support/fake_connection.rb: -------------------------------------------------------------------------------- 1 | require "dea/promise" 2 | 3 | class FakeConnection 4 | 5 | attr_reader :host_ip, :path 6 | 7 | def initialize 8 | @responses = {} 9 | end 10 | 11 | def set_fake_properties(options) 12 | @host_ip = options[:host_ip] 13 | @path = options[:path] 14 | end 15 | 16 | def set_fake_response(request_type, response) 17 | @responses[request_type] = response 18 | end 19 | 20 | def promise_call(request) 21 | Dea::Promise.new do |promise| 22 | promise.deliver(@responses[request.class]) 23 | end 24 | end 25 | end -------------------------------------------------------------------------------- /spec/support/fake_emitter.rb: -------------------------------------------------------------------------------- 1 | class FakeEmitter 2 | attr_reader :messages, :error_messages 3 | 4 | def initialize 5 | @messages = Hash.new 6 | @error_messages = Hash.new 7 | end 8 | 9 | def emit(app_id, message) 10 | return unless app_id && message && message.strip.length > 0 11 | unless @messages[app_id] 12 | @messages[app_id] = [] 13 | end 14 | @messages[app_id].push(message) 15 | end 16 | 17 | def emit_error(app_id, message) 18 | return unless app_id && message && message.strip.length > 0 19 | unless @error_messages[app_id] 20 | @error_messages[app_id] = [] 21 | end 22 | @error_messages[app_id].push(message) 23 | end 24 | 25 | def emit_value_metric(name, value, unit) 26 | return unless name && value && unit 27 | unless @messages[name] 28 | @messages[name] = [] 29 | end 30 | @messages[name].push({:value => value, :unit => unit}) 31 | end 32 | 33 | def emit_counter(name, delta) 34 | return unless name && delta 35 | unless @messages[name] 36 | @messages[name] = [] 37 | end 38 | @messages[name].push({:delta => delta}) 39 | end 40 | 41 | def emit_container_metric(app_id, instanceIndex, cpuPercentage, memoryBytes, diskBytes) 42 | return unless app_id && instanceIndex && cpuPercentage && memoryBytes && diskBytes 43 | unless @messages[app_id] 44 | @messages[app_id] = [] 45 | end 46 | @messages[app_id].push( 47 | { 48 | :app_id => app_id, 49 | :instanceIndex => instanceIndex, 50 | :cpuPercentage => cpuPercentage, 51 | :memoryBytes => memoryBytes, 52 | :diskBytes => diskBytes 53 | }) 54 | end 55 | 56 | def reset 57 | @messages = Hash.new 58 | @error_messages = Hash.new 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /spec/support/integration_helpers/buildpack_helpers.rb: -------------------------------------------------------------------------------- 1 | require "socket" 2 | 3 | module BuildpackHelpers 4 | def fake_buildpack_url(buildpack_name) 5 | "http://#{file_server_address}/buildpacks/#{buildpack_name}/.git" 6 | end 7 | 8 | def setup_fake_buildpack(buildpack_name) 9 | Dir.chdir("spec/fixtures/fake_buildpacks/#{buildpack_name}") do 10 | `rm -rf .git` 11 | `git init` 12 | `git add . && git add -A` 13 | `git commit -am "fake commit"` 14 | `git branch a_branch` 15 | `git tag -m 'annotated tag' a_tag` 16 | `git tag a_lightweight_tag` 17 | `git update-server-info` 18 | end 19 | end 20 | 21 | def download_tgz(url) 22 | Dir.mktmpdir do |dir| 23 | system("curl --silent --show-error #{url} > #{dir}/staged_app.tgz") or raise "Could not download staged_app.tgz" 24 | Dir.chdir(dir) do 25 | system("tar xzf staged_app.tgz") or raise "Could not untar staged_app.tgz" 26 | end 27 | 28 | yield dir 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/support/integration_helpers/nats_helper.rb: -------------------------------------------------------------------------------- 1 | require "yajl" 2 | require "timeout" 3 | 4 | class NatsHelper 5 | def initialize(dea_config) 6 | @dea_config = dea_config 7 | end 8 | 9 | def connected? 10 | NATS.connected? 11 | end 12 | 13 | def request(key, data, options={}) 14 | send_message(:request, key, data, options) 15 | end 16 | 17 | def publish(key, data, options={}) 18 | send_message(:publish, key, data, options) 19 | end 20 | 21 | def with_nats(&blk) 22 | NATS.start(:uri => nats_servers, &blk) 23 | end 24 | 25 | def with_subscription(key) 26 | response = nil 27 | 28 | with_nats do 29 | yield if block_given? 30 | 31 | NATS.subscribe(key) do |resp| 32 | response = resp 33 | NATS.stop 34 | end 35 | end 36 | 37 | Yajl::Parser.parse(response) if response 38 | end 39 | 40 | def make_blocking_request(key, message, number_of_expected_responses, timeout=10) 41 | responses = [] 42 | 43 | with_nats do 44 | sid = NATS.request(key, Yajl::Encoder.encode(message), :max => number_of_expected_responses) do |response| 45 | response = Yajl::Parser.parse(response) 46 | responses << response 47 | yield(responses.count - 1, response) if block_given? 48 | NATS.stop if responses.count == number_of_expected_responses 49 | end 50 | 51 | NATS.timeout(sid, timeout) do 52 | NATS.stop 53 | fail "Timeout getting response" 54 | end 55 | end 56 | 57 | responses 58 | end 59 | 60 | private 61 | 62 | def send_message(method, key, data, options) 63 | response = nil 64 | 65 | if options[:async] 66 | NATS.public_send(method, key, Yajl::Encoder.encode(data)) do |resp| 67 | response = resp 68 | end 69 | else 70 | with_nats do 71 | sid = NATS.public_send(method, key, Yajl::Encoder.encode(data)) do |resp| 72 | response = resp 73 | NATS.stop 74 | end 75 | 76 | if timeout = options[:timeout] 77 | NATS.timeout(sid, timeout) { NATS.stop } 78 | end 79 | end 80 | end 81 | 82 | Yajl::Parser.parse(response) if response 83 | end 84 | 85 | def nats_servers 86 | @dea_config["nats_servers"] 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /spec/support/integration_helpers/process_helpers.rb: -------------------------------------------------------------------------------- 1 | require 'socket' 2 | 3 | module ProcessHelpers 4 | def run_cmd(cmd, opts={}) 5 | project_path = File.join(File.dirname(__FILE__), "../../..") 6 | spawn_opts = { 7 | :chdir => project_path, 8 | :out => opts[:debug] ? :out : "/dev/null", 9 | :err => opts[:debug] ? :out : "/dev/null", 10 | } 11 | 12 | Process.spawn(cmd, spawn_opts).tap do |pid| 13 | if opts[:wait] 14 | Process.wait(pid) 15 | raise "`#{cmd}` exited with #{$?}" unless $?.success? 16 | end 17 | end 18 | end 19 | 20 | def graceful_kill(pid) 21 | Process.kill("TERM", pid) 22 | Timeout.timeout(30) do 23 | while process_alive?(pid) do 24 | end 25 | end 26 | rescue Timeout::Error 27 | Process.kill("KILL", pid) 28 | end 29 | 30 | def merciless_kill(pid) 31 | Process.kill("KILL", pid) 32 | end 33 | 34 | def process_alive?(pid) 35 | Process.kill(0, pid) 36 | true 37 | rescue Errno::ESRCH 38 | false 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/support/mock_class.rb: -------------------------------------------------------------------------------- 1 | module MockClass 2 | module Methods 3 | # Safely override existing method 4 | def overrides(name, &blk) 5 | _check_instance_method_exists!(name) 6 | 7 | # method made from blk will have different parameters from blk parameters 8 | alias_method "overriden_#{name}", name 9 | overriden_method = instance_method("overriden_#{name}") 10 | 11 | define_method(name, &blk) 12 | replacement_method = instance_method(name) 13 | 14 | unless replacement_method.parameters == overriden_method.parameters 15 | raise <<-ERROR 16 | Instance method parameters for '#{name}' do not match overriden method's parameters: 17 | New: #{replacement_method.parameters} 18 | (on #{blk.source_location.join(":")}) 19 | Old: #{overriden_method.parameters} 20 | (on #{overriden_method.source_location.join(":")}) 21 | ERROR 22 | end 23 | end 24 | 25 | # Add helper method to use in tests 26 | def add(name, &blk) 27 | _check_instance_method_does_not_exist!(name) 28 | define_method(name, &blk) 29 | end 30 | 31 | private 32 | 33 | def _check_instance_method_exists!(name) 34 | raise "Public instance method '#{name}' is not defined on class '#{self.name}'" \ 35 | unless _instance_methods_to_check(name).include?(name.to_sym) 36 | end 37 | 38 | def _check_instance_method_does_not_exist!(name) 39 | raise "Public instance method '#{name}' is already defined on class '#{self.name}'" \ 40 | if _instance_methods_to_check(name).include?(name.to_sym) 41 | end 42 | 43 | def _instance_methods_to_check(name) 44 | methods = (name == :initialize) ? private_instance_methods : public_instance_methods 45 | end 46 | end 47 | 48 | def self.define(mock_class_name, klass, &blk) 49 | mock_class = Class.new(klass) do 50 | extend Methods 51 | instance_eval(&blk) 52 | end 53 | 54 | Kernel.const_set(mock_class_name, mock_class) 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /spec/support/nats.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "nats/client" 4 | require File.expand_path("mock_class", File.dirname(__FILE__)) 5 | 6 | # Defined specifically for NatsClientMock 7 | class NatsInstance 8 | # NATS is actually a module which gets made into a class 9 | # and then instantiated by one of the EM.connect* methods 10 | include NATS 11 | end 12 | 13 | MockClass.define(:NatsClientMock, NatsInstance) do 14 | overrides :initialize do |options| 15 | @options = options 16 | @subscriptions = Hash.new { |h, k| h[k] = [] } 17 | @request_inboxes = {} 18 | @requests = 0 19 | end 20 | 21 | overrides :subscribe do |subject, opts={}, &callback| 22 | @subscriptions[subject] << callback 23 | callback # Consider block a subscription id 24 | end 25 | 26 | overrides :unsubscribe do |sid, opt_max=nil| 27 | @subscriptions.each do |_, blks| 28 | blks.delete(sid) 29 | end 30 | end 31 | 32 | overrides :publish do |subject, msg=nil, opt_reply=nil, &blk| 33 | receive_message(subject, msg, opt_reply) 34 | end 35 | 36 | overrides :request do |subject, data=nil, opts={}, &cb| 37 | inbox = nil 38 | 39 | @requests += 1 40 | 41 | if cb 42 | inbox = "nats_mock_request_#{@requests}" 43 | subscribe(inbox, &cb) 44 | end 45 | 46 | @request_inboxes[subject] = inbox 47 | 48 | publish(subject, data, inbox) 49 | end 50 | 51 | add :receive_message do |subject, data = {}, respond_to = nil| 52 | if data.kind_of?(String) 53 | raw_data = data 54 | else 55 | raw_data = Yajl::Encoder.encode(data) 56 | end 57 | 58 | @subscriptions[subject].each do |blk| 59 | blk.call(raw_data, respond_to) 60 | end 61 | end 62 | 63 | add :respond_to_channel do |subject, data = {}, respond_to = nil| 64 | if (subscription = @request_inboxes[subject]) 65 | if data.kind_of?(String) 66 | raw_data = data 67 | else 68 | raw_data = Yajl::Encoder.encode(data) 69 | end 70 | 71 | @subscriptions[subscription].each do |blk| 72 | blk.call(raw_data, respond_to) 73 | end 74 | end 75 | end 76 | end 77 | 78 | module NatsClientMockHelpers 79 | def stub_nats 80 | before do 81 | @nats_mock = NatsClientMock.new({}) 82 | allow(NATS).to receive(:connect) do |opts| 83 | expect(opts[:max_reconnect_attempts]).to eq(-1) 84 | nats_mock 85 | end 86 | end 87 | 88 | attr_reader :nats_mock 89 | end 90 | end 91 | 92 | RSpec.configure do |rspec_config| 93 | rspec_config.extend NatsClientMockHelpers 94 | end 95 | -------------------------------------------------------------------------------- /spec/support/promise.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "dea/promise" 4 | 5 | module Helpers 6 | def delivering_promise(value = nil) 7 | Dea::Promise.new { |p| p.deliver(value) } 8 | end 9 | 10 | def failing_promise(value) 11 | Dea::Promise.new { |p| p.fail(value) } 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/support/staging_spec_helpers.rb: -------------------------------------------------------------------------------- 1 | require "tmpdir" 2 | 3 | module StagingSpecHelpers 4 | def app_fixture(name) 5 | @app_fixture = name.to_s 6 | end 7 | 8 | def stage(config = {}) 9 | raise "Call 'app_fixture :name_of_app' before staging" unless @app_fixture 10 | working_dir = Dir.mktmpdir("#{@app_fixture}-staged") 11 | stringified_config = {} 12 | config.each_pair { |k, v| stringified_config[k.to_s] = v } 13 | 14 | config = { 15 | "source_dir" => app_source, 16 | "dest_dir" => working_dir, 17 | "environment" => [] 18 | }.merge(stringified_config) 19 | 20 | Buildpacks::Buildpack.new(config).stage_application 21 | Dir.chdir(working_dir) do 22 | yield Pathname.new(working_dir) if block_given? 23 | end 24 | ensure 25 | FileUtils.rm_r(working_dir) if working_dir 26 | end 27 | 28 | def app_source 29 | app_fixture_base_directory.join(@app_fixture).to_s 30 | end 31 | 32 | private 33 | 34 | def app_fixture_base_directory 35 | Pathname.new(fixture("apps")) 36 | end 37 | end -------------------------------------------------------------------------------- /spec/support/tmpdir.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "tmpdir" 4 | 5 | shared_context "tmpdir" do 6 | attr_reader :tmpdir 7 | 8 | around do |example| 9 | Dir.mktmpdir do |tmpdir| 10 | # Store path to tmpdir 11 | @tmpdir = File.realpath(tmpdir) 12 | 13 | # Run example 14 | example.run 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/unit/bootstrap/discovery_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "dea/bootstrap" 5 | 6 | describe Dea do 7 | include_context "bootstrap_setup" 8 | 9 | before do 10 | allow(bootstrap).to receive(:setup_directory_server).and_call_original 11 | end 12 | 13 | def discover_message(opts = {}) 14 | { "runtime" => "test1", 15 | "droplet" => 0, 16 | "limits" => { 17 | "mem" => 10, 18 | "disk" => 10, 19 | } 20 | }.merge(opts) 21 | end 22 | 23 | def verify_hello_message(bootstrap, hello) 24 | expect(hello).to_not be_nil 25 | expect(hello["id"]).to eq(bootstrap.uuid) 26 | expect(hello["ip"]).to eq(bootstrap.local_ip) 27 | expect(hello["version"]).to eq(Dea::VERSION) 28 | end 29 | 30 | def verify_status_message(bootstrap, status) 31 | verify_hello_message(bootstrap, status) 32 | 33 | %W[max_memory reserved_memory used_memory num_clients].each do |k| 34 | expect(status.has_key?(k)).to be true 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/unit/buildpack/procfile_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Buildpacks::Procfile, :type => :buildpack do 4 | let(:contents) { {"web" => "something"} } 5 | let(:path) { procfile.path } 6 | let(:procfile) do 7 | file = Tempfile.new('foo') 8 | file.write(contents.to_yaml) 9 | file.close 10 | file 11 | end 12 | 13 | describe "#contents" do 14 | it "should only read the file contents once" do 15 | allow(YAML).to receive(:load).with(anything).once.and_call_original 16 | file = Buildpacks::Procfile.new(path) 17 | file.contents 18 | file.contents 19 | end 20 | 21 | context "when its on the filesystem" do 22 | it "loads the contents" do 23 | expect(Buildpacks::Procfile.new(path).contents).to eq contents 24 | end 25 | end 26 | 27 | context "when procfile is not a hash" do 28 | let(:contents) { "some non hash thing" } 29 | it "raises an exception" do 30 | expect { Buildpacks::Procfile.new(path).contents }.to raise_error ArgumentError 31 | end 32 | end 33 | 34 | context "when its not found" do 35 | let(:path) { "/non_existant_file" } 36 | it "does not fail" do 37 | expect(Buildpacks::Procfile.new(path).contents).to be_nil 38 | end 39 | end 40 | end 41 | 42 | describe "#web" do 43 | it "get the web key" do 44 | expect(Buildpacks::Procfile.new(path).web).to eq "something" 45 | end 46 | 47 | context "when the contents does not exist" do 48 | let(:path) { "/non_existant_file" } 49 | it "returns nil" do 50 | expect(Buildpacks::Procfile.new(path).web).to be_nil 51 | end 52 | end 53 | 54 | context "when web does not exist" do 55 | let(:contents) { {"foobar" => "something"} } 56 | it "returns nil" do 57 | expect(Buildpacks::Procfile.new(path).web).to be_nil 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /spec/unit/directory_server/hmac_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | require "dea/directory_server/hmac_helper" 3 | 4 | describe HMACHelper do 5 | subject { HMACHelper.new("key") } 6 | 7 | # echo -n "value" | openssl dgst -sha512 -hmac "key" 8 | VALUE_HMAC = "86951dc765bef95f9474669cd18df7705d99ae47ea3e76a2ca4c22f71656f42ea66e3acdc898c93f475009fa599d0bb83bd5365f36a9cb92c570708f8de5fae8" 9 | 10 | describe "#initialize" do 11 | it "raises error when key is nil" do 12 | expect do 13 | HMACHelper.new(nil) 14 | end.to raise_error(ArgumentError, /key must not be nil/) 15 | end 16 | end 17 | 18 | describe "#create" do 19 | it "returns sha1 hmac value" do 20 | expect(subject.create("value")).to eq(VALUE_HMAC) 21 | end 22 | end 23 | 24 | describe "#compare" do 25 | context "when string hmac matches given hmac" do 26 | it "returns true" do 27 | expect(subject.compare(VALUE_HMAC, "value")).to be true 28 | end 29 | end 30 | 31 | context "when string hmac does not given hmac" do 32 | it "returns false" do 33 | expect(subject.compare(VALUE_HMAC, "value1")).to be false 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/unit/droplet_registry_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "digest/sha1" 5 | require "dea/droplet_registry" 6 | 7 | describe Dea::DropletRegistry do 8 | include_context "tmpdir" 9 | 10 | let(:payload) do 11 | "droplet" 12 | end 13 | 14 | let(:sha1) do 15 | Digest::SHA1.hexdigest(payload) 16 | end 17 | 18 | subject(:droplet_registry) do 19 | Dea::DropletRegistry.new(tmpdir) 20 | end 21 | 22 | it 'is a hash' do 23 | expect(droplet_registry).to be_a(Hash) 24 | end 25 | 26 | it "should create Droplet objects when indexed" do 27 | expect(droplet_registry[sha1]).to be_kind_of(Dea::Droplet) 28 | end 29 | 30 | it "should initialize with existing droplets" do 31 | sha1s = 3.times.map do |i| 32 | Digest::SHA1.hexdigest(i.to_s).tap do |sha1| 33 | FileUtils.mkdir_p(File.join(tmpdir, sha1)) 34 | end 35 | end 36 | 37 | droplet_registry = Dea::DropletRegistry.new(tmpdir) 38 | expect(droplet_registry.size).to eq(3) 39 | expect(droplet_registry.keys.sort).to eq(sha1s.sort) 40 | end 41 | 42 | it "raises when the sha is nil" do 43 | expect { 44 | droplet_registry[nil] 45 | }.to raise_error(ArgumentError, /sha cannot be nil/) 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /spec/unit/env/strategy_chooser_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | require "dea/env/strategy_chooser" 3 | 4 | module Dea 5 | class Env 6 | describe StrategyChooser do 7 | let(:task) { double } 8 | subject(:env_strategy_chooser) { StrategyChooser.new(message, task) } 9 | let(:strategy) { double("strategy")} 10 | 11 | before do 12 | allow(Staging::Env).to receive(:new).and_return(strategy) 13 | allow(Starting::Env).to receive(:new).and_return(strategy) 14 | end 15 | 16 | context "when a staging message is provided" do 17 | let(:message) { StagingMessage.new({}) } 18 | 19 | it "instantiates the staging strategy" do 20 | expect(env_strategy_chooser.strategy).to eq(strategy) 21 | expect(Staging::Env).to have_received(:new).with(message, task) 22 | end 23 | end 24 | 25 | context "when a non-staging message is provided" do 26 | let(:message) { double("non staging message") } 27 | 28 | it "instantiates the staging strategy" do 29 | expect(env_strategy_chooser.strategy).to eq(strategy) 30 | expect(Starting::Env).to have_received(:new).with(message, task) 31 | end 32 | end 33 | end 34 | end 35 | end -------------------------------------------------------------------------------- /spec/unit/health_check/port_open_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "dea/health_check/port_open" 5 | 6 | describe Dea::HealthCheck::PortOpen do 7 | let(:host) { "127.0.0.1" } 8 | let(:port) { Dea.grab_ephemeral_port } 9 | 10 | def start_server 11 | EM.start_server(host, port) 12 | end 13 | 14 | it "should succed if port check succeeds" do 15 | ok = run_health_check(host, port, 0.1) do 16 | start_server 17 | end 18 | 19 | expect(ok).to be true 20 | end 21 | 22 | it "should succed if someone starts listening on the port" do 23 | ok = run_health_check(host, port, 0.1) do 24 | EM.add_timer(0.04) { start_server } 25 | end 26 | 27 | expect(ok).to be true 28 | end 29 | 30 | it "should fail if no-one is listening on the port" do 31 | start = Time.now 32 | ok = run_health_check(host, port, 0.1) 33 | elapsed = Time.now - start 34 | 35 | expect(ok).to be false 36 | expect(elapsed).to be_within(0.2).of(0.1) 37 | end 38 | 39 | def run_health_check(host, port, timeout, &blk) 40 | success = false 41 | 42 | with_event_machine(:timeout => 1) do 43 | blk.call if blk 44 | 45 | Dea::HealthCheck::PortOpen.new(host, port, 0.02) do |hc| 46 | hc.callback do 47 | success = true 48 | EM.stop 49 | end 50 | 51 | hc.errback do 52 | success = false 53 | EM.stop 54 | end 55 | 56 | hc.timeout(timeout) 57 | end 58 | end 59 | 60 | success 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /spec/unit/health_check/state_file_ready_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require "spec_helper" 4 | require "yajl" 5 | require "dea/health_check/state_file_ready" 6 | 7 | describe Dea::HealthCheck::StateFileReady do 8 | include_context "tmpdir" 9 | 10 | let(:state_file_path) { File.join(tmpdir, "state.json") } 11 | 12 | it "fails if the file never exists" do 13 | expect(run_health_check(state_file_path, 0.1)).to eq("failure") 14 | end 15 | 16 | it "fails if the file exists but the state is never 'RUNNING'" do 17 | write_state_file(state_file_path, "CRASHED") 18 | expect(run_health_check(state_file_path, 0.1)).to eq("failure") 19 | end 20 | 21 | it "fails if the state file is corrupted" do 22 | File.open(state_file_path, "w+") { |f| f.write("{{{") } 23 | expect(run_health_check(state_file_path, 0.1)).to eq("failure") 24 | end 25 | 26 | it "succeeds if the file exists prior to starting the health check" do 27 | write_state_file(state_file_path, "RUNNING") 28 | expect(run_health_check(state_file_path, 0.1)).to eq("success") 29 | end 30 | 31 | it "succeeds if the file exists before the timeout" do 32 | create_file = lambda do 33 | EM.add_timer(0.04) do 34 | write_state_file(state_file_path, "RUNNING") 35 | end 36 | end 37 | expect(run_health_check(state_file_path, 0.1, &create_file)).to eq("success") 38 | end 39 | 40 | def run_health_check(path, timeout, &before_health_check) 41 | result = nil 42 | 43 | with_event_machine(:timeout => 1) do 44 | before_health_check.call unless before_health_check.nil? 45 | 46 | Dea::HealthCheck::StateFileReady.new(path, 0.02) do |hc| 47 | hc.callback do 48 | result = "success" 49 | EM.stop 50 | end 51 | 52 | hc.errback do 53 | result = "failure" 54 | EM.stop 55 | end 56 | 57 | hc.timeout(timeout) 58 | end 59 | end 60 | 61 | result 62 | end 63 | 64 | def write_state_file(path, state) 65 | File.open(path, "w+") do |f| 66 | f.write(Yajl::Encoder.encode({ "state" => state })) 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /spec/unit/pid_file_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require 'spec_helper' 4 | require 'dea/pid_file' 5 | 6 | describe '.process_running' do 7 | end 8 | 9 | describe Dea::PidFile do 10 | before :all do 11 | @pid_file = "/tmp/pidfile_test_%d_%d_%d" % [Process.pid(), Time.now().to_i(), rand(1000)] 12 | end 13 | 14 | after :each do 15 | FileUtils.rm_f(@pid_file) 16 | end 17 | 18 | it "should create a pidfile if one doesn't exist" do 19 | Dea::PidFile.new(@pid_file) 20 | expect(File.exists?(@pid_file)).to be true 21 | end 22 | 23 | 24 | it "should overwrite pid file if pid file exists and contained pid isn't running" do 25 | fork { Dea::PidFile.new(@pid_file) } 26 | 27 | Process.wait() 28 | Dea::PidFile.new(@pid_file) 29 | pid = File.open(@pid_file) {|f| pid = f.read().strip().to_i()} 30 | expect(pid).to eq Process.pid() 31 | end 32 | 33 | it "should throw exception if pid file exists and contained pid has running process" do 34 | child_pid = fork { 35 | Dea::PidFile.new(@pid_file) 36 | Signal.trap('HUP') { exit } 37 | while true; end 38 | } 39 | sleep(1) 40 | thrown = false 41 | begin 42 | Dea::PidFile.new(@pid_file) 43 | rescue Dea::PidFile::ProcessRunningError => e 44 | thrown = true 45 | end 46 | Process.kill('HUP', child_pid) 47 | Process.wait() 48 | expect(thrown).to be true 49 | end 50 | 51 | it "shouldn't throw an exception if current process's pid is in pid file" do 52 | expect{Dea::PidFile.new(@pid_file)}.to_not raise_error 53 | expect{Dea::PidFile.new(@pid_file)}.to_not raise_error 54 | end 55 | 56 | describe '#unlink' do 57 | it "should remove pidfile correctly" do 58 | pf = Dea::PidFile.new(@pid_file) 59 | pf.unlink() 60 | expect(File.exists?(@pid_file)).to be false 61 | end 62 | end 63 | 64 | describe '#unlink_at_exit' do 65 | it "should remove pidfile upon exit", unix_only: true do 66 | child_pid = fork { 67 | pf = Dea::PidFile.new(@pid_file) 68 | pf.unlink_at_exit() 69 | Signal.trap('HUP') { exit } 70 | while true; end 71 | } 72 | sleep 1 73 | Process.kill('HUP', child_pid) 74 | Process.wait() 75 | expect(File.exists?(@pid_file)).to be false 76 | end 77 | end 78 | end 79 | 80 | -------------------------------------------------------------------------------- /spec/unit/responders/http_staging_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'dea/staging/staging_task_registry' 4 | 5 | require 'dea/responders/http_staging' 6 | require 'dea/responders/staging' 7 | 8 | require 'dea/utils/cloud_controller_client' 9 | 10 | describe Dea::Responders::HttpStaging do 11 | let(:cc_client) { double(Dea::CloudControllerClient) } 12 | let(:staging_message) { double(StagingMessage) } 13 | 14 | let(:request) { {'app_id' => 'app_id'} } 15 | let(:staging_task) do 16 | double(:staging_task, 17 | task_id: "task-id", 18 | streaming_log_url: "log url", 19 | ) 20 | end 21 | 22 | let(:stager) { double(Dea::Responders::Staging, :create_task => staging_task) } 23 | 24 | subject { described_class.new(stager, cc_client) } 25 | 26 | describe '#handle' do 27 | let(:staging_message) { StagingMessage.new(nil) } 28 | 29 | before do 30 | allow(staging_message).to receive(:set_responder).and_call_original 31 | allow(StagingMessage).to receive(:new).with(request).and_return(staging_message) 32 | end 33 | 34 | it 'sets the responder to the cloud controller client' do 35 | allow(staging_task).to receive(:start) do 36 | staging_message.respond({}) do 37 | called = true 38 | end 39 | end 40 | 41 | expect(cc_client).to receive(:send_staging_response) 42 | 43 | subject.handle(request) 44 | end 45 | 46 | it 'starts the staging task' do 47 | allow(staging_message).to receive(:set_responder) 48 | expect(staging_task).to receive(:start) 49 | subject.handle(request) 50 | end 51 | 52 | context 'when creating a staging task fails' do 53 | before do 54 | allow(stager).to receive(:create_task).and_return(nil) 55 | end 56 | 57 | it 'does not start any staging task' do 58 | expect(staging_task).to_not receive(:start) 59 | subject.handle(request) 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /spec/unit/staging/buildpacks_message_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | require "dea/staging/buildpacks_message" 3 | 4 | describe BuildpacksMessage do 5 | 6 | subject { BuildpacksMessage.new(message) } 7 | 8 | context "when the message is empty" do 9 | let(:message) { [] } 10 | 11 | it "should have an empty buildpack list" do 12 | expect(subject.buildpacks).to eq([]) 13 | end 14 | end 15 | 16 | context "when admin build packs are specified" do 17 | let(:message) do 18 | [ 19 | { 20 | "url" => "http://www.example.com/buildpacks/uri/first", 21 | "key" => "first" 22 | }, 23 | { 24 | "url" => "http://www.example.com/buildpacks/uri/second", 25 | "key" => "second" 26 | } 27 | ] 28 | end 29 | 30 | it "should contain a list of buildpacks" do 31 | expect(subject.buildpacks).to eq([ 32 | { 33 | url: URI("http://www.example.com/buildpacks/uri/first"), 34 | key: "first" 35 | }, 36 | { 37 | url: URI("http://www.example.com/buildpacks/uri/second"), 38 | key: "second" 39 | } 40 | ]) 41 | end 42 | 43 | it "should ignore invalid buildpack urls" do 44 | message[0]["url"] = nil 45 | 46 | expect(subject.buildpacks).to eq([ 47 | { 48 | url: URI("http://www.example.com/buildpacks/uri/second"), 49 | key: "second" 50 | } 51 | ]) 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /spec/unit/staging/env_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'dea/staging/env' 3 | 4 | module Dea::Staging 5 | describe Env do 6 | let(:staging_message) { double(:staging_message, mem_limit: 'fake_mem_limit') } 7 | let(:task) { double('task', staging_config: {}, staging_timeout: 'fake_timeout') } 8 | 9 | subject(:env) {Env.new(staging_message, task)} 10 | 11 | describe 'system environment variables' do 12 | subject(:system_environment_variables) { env.system_environment_variables } 13 | 14 | it 'has the correct values' do 15 | expect(system_environment_variables).to eql([ 16 | %w(STAGING_TIMEOUT fake_timeout), 17 | %w(MEMORY_LIMIT fake_mem_limitm), 18 | ]) 19 | end 20 | end 21 | 22 | describe 'vcap_application' do 23 | subject(:vcap_application) { env.vcap_application } 24 | it 'is empty' do 25 | expect(vcap_application).to eql({}) 26 | end 27 | end 28 | 29 | it 'has a message' do 30 | expect(env.message).to eql(staging_message) 31 | end 32 | 33 | it 'has an instance' do 34 | expect(env.staging_task).to eql(task) 35 | end 36 | end 37 | end 38 | 39 | 40 | -------------------------------------------------------------------------------- /spec/unit/staging/staging_task_registry_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | require "dea/staging/staging_task" 4 | require "dea/staging/staging_task_registry" 5 | 6 | describe Dea::StagingTaskRegistry do 7 | let(:bootstrap) { double(:bootstrap, :config => {}) } 8 | let(:task_1) { Dea::StagingTask.new(bootstrap, nil, StagingMessage.new(valid_staging_attributes), []) } 9 | let(:task_2) { Dea::StagingTask.new(bootstrap, nil, StagingMessage.new(valid_staging_attributes), []) } 10 | 11 | it_behaves_like :handles_registry_enumerations 12 | 13 | describe "#register" do 14 | it "adds task to the registry" do 15 | expect { 16 | subject.register(task_1) 17 | }.to change { subject.registered_task(task_1.task_id) }.from(nil).to(task_1) 18 | end 19 | end 20 | 21 | describe "#unregister" do 22 | context "when task was previously registered" do 23 | before { subject.register(task_1) } 24 | 25 | it "removes task from the registry" do 26 | expect { 27 | subject.unregister(task_1) 28 | }.to change { subject.registered_task(task_1.task_id) }.from(task_1).to(nil) 29 | end 30 | end 31 | 32 | context "when task was not previously registered" do 33 | it "does nothing" do 34 | subject.unregister(task_1) 35 | end 36 | end 37 | end 38 | 39 | describe "#tasks" do 40 | before { subject.register(task_1) } 41 | before { subject.register(task_2) } 42 | 43 | it "returns all previously registered tasks" do 44 | expect(subject.tasks).to_not eq([task_2, task_1]) 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /spec/unit/starting/instance_uri_updater_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | require "dea/starting/instance_uri_updater" 3 | require "dea/starting/instance" 4 | 5 | describe Dea::InstanceUriUpdater do 6 | let(:app_uris) { ["app.cfapps.io", "app.run.pivotal.io"] } 7 | let(:instance) { instance_double("Dea::Instance") } 8 | let(:router_client) { instance_double("Dea::RouterClient") } 9 | 10 | before do 11 | allow(instance).to receive(:application_uris).and_return(app_uris) 12 | allow(instance).to receive(:application_uris=).and_return(app_uris) 13 | end 14 | 15 | it "adds new uris" do 16 | new_uris = ["app.cfapps.io", "app.run.pivotal.io", "new.cfapps.io"] 17 | updater = Dea::InstanceUriUpdater.new(instance, new_uris) 18 | expect(router_client).to receive(:register_instance).with(instance, uris: ["new.cfapps.io"]) 19 | expect(updater.update(router_client)).to be true 20 | end 21 | 22 | it "removes obsolete uris" do 23 | new_uris = ["app.cfapps.io"] 24 | updater = Dea::InstanceUriUpdater.new(instance, new_uris) 25 | expect(router_client).to receive(:unregister_instance).with(instance, uris: ["app.run.pivotal.io"]) 26 | expect(updater.update(router_client)).to be true 27 | end 28 | 29 | it "updates the app instance with the current uris" do 30 | new_uris = ["app.cfapps.io", "app.example.com"] 31 | updater = Dea::InstanceUriUpdater.new(instance, new_uris) 32 | 33 | expect(router_client).to receive(:unregister_instance).with(instance, uris: ["app.run.pivotal.io"]) 34 | expect(router_client).to receive(:register_instance).with(instance, uris: ["app.example.com"]) 35 | expect(instance).to receive(:application_uris=).with(new_uris) 36 | updater.update(router_client) 37 | end 38 | 39 | it "does nothing if the uris have not changed" do 40 | updater = Dea::InstanceUriUpdater.new(instance, app_uris) 41 | expect(updater.update(router_client)).to be false 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/unit/user_facing_errors_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | require "dea/user_facing_errors" 3 | 4 | module Dea 5 | describe HealthCheckFailed do 6 | describe "#to_s" do 7 | it 'returns the failure string message' do 8 | expect(HealthCheckFailed.new.to_s).to eq "failed to accept connections within health check timeout" 9 | end 10 | end 11 | end 12 | 13 | describe MissingStartCommand do 14 | describe "#to_s" do 15 | it 'returns the failure string message' do 16 | expect(MissingStartCommand.new.to_s).to eq "missing start command" 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/unit/utils/hm9000_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'dea/utils/hm9000' 3 | require 'dea/protocol' 4 | 5 | describe HM9000 do 6 | around do |example| 7 | with_event_machine { example.call } 8 | end 9 | 10 | let(:to_uri) { URI("https://a.b.c.d:12345/") } 11 | 12 | let(:polling_timeout_in_second) { 3 } 13 | let(:timeout) { 10 } 14 | 15 | subject { HM9000.new( 16 | to_uri, 17 | fixture('/certs/hm9000_client.key'), 18 | fixture('/certs/hm9000_client.cert'), 19 | fixture('/certs/hm9000_client.crt'), 20 | timeout, 21 | )} 22 | 23 | before do 24 | allow(EM).to receive(:defer) do |operation, &_| 25 | operation.call 26 | end 27 | end 28 | 29 | describe "#send_heartbeat" do 30 | let(:heartbeat) { Dea::Protocol::V1::HeartbeatResponse.generate("dea-uuid", []) } 31 | 32 | before { WebMock.disable_net_connect! } 33 | 34 | context 'when the request succeeds' do 35 | before do 36 | stub_request(:post, "https://a.b.c.d:12345/dea/heartbeat").with(:body => '{"droplets":[],"dea":"dea-uuid"}').to_return(:status => status) 37 | end 38 | 39 | context 'when the status is 202' do 40 | let(:status) {202} 41 | 42 | it "creates the correct request with the heartbeat in json" do 43 | subject.send_heartbeat(heartbeat) do |response| 44 | expect(response.status).to eq(status) 45 | done 46 | end 47 | end 48 | end 49 | 50 | context 'when the status is not 202' do 51 | let(:status) {401} 52 | 53 | it 'returns the status' do 54 | subject.send_heartbeat(heartbeat) do |response| 55 | expect(response.status).to eq(status) 56 | done 57 | end 58 | end 59 | end 60 | end 61 | 62 | context 'when the request raises an exception' do 63 | before { WebMock.allow_net_connect! } 64 | 65 | it 'reports the error' do 66 | expect(subject.logger).to receive(:error).with('hm9000.heartbeat.failed', hash_including(:error)) 67 | subject.send_heartbeat(heartbeat) 68 | done 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /spec/unit/utils/uri_cleaner_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'dea/utils/uri_cleaner' 3 | 4 | describe URICleaner do 5 | 6 | describe "strings as uris" do 7 | context "when both username and password are supplied" do 8 | let (:uri) { "nats://user:password@server:4222" } 9 | 10 | it "removes the user and password" do 11 | expect(URICleaner.clean(uri)).to eq("nats://server:4222") 12 | end 13 | end 14 | 15 | context "when only the username is supplied" do 16 | let (:uri) { "https://frank@somehost.com:8080" } 17 | 18 | it "removes the user" do 19 | expect(URICleaner.clean(uri)).to eq("https://somehost.com:8080") 20 | end 21 | end 22 | 23 | context "when the uri is opaque" do 24 | let (:uri) { "mailto:user:pass@example.com" } 25 | 26 | it "does not modify opaque urls" do 27 | expect(URICleaner.clean(uri)).to eq(uri) 28 | end 29 | end 30 | end 31 | 32 | describe "URIs as uris" do 33 | let (:uri) { "nats://user:password@example.com:4222" } 34 | let (:uri_object) { URI(uri) } 35 | 36 | it "does not modify the original uri object" do 37 | expect { 38 | URICleaner.clean(uri_object) 39 | }.to_not change { 40 | uri_object 41 | } 42 | end 43 | 44 | it "returns a string" do 45 | expect(URICleaner.clean(uri_object)).to be_a(String) 46 | end 47 | 48 | it "removes the username and password" do 49 | expect(URICleaner.clean(uri_object)).to eq("nats://example.com:4222") 50 | end 51 | 52 | context "when the uri is opaque" do 53 | let (:uri) { "mailto:user:something@example.com" } 54 | 55 | it "does not modify the uri" do 56 | expect(URICleaner.clean(uri_object)).to eq(uri) 57 | end 58 | end 59 | end 60 | 61 | context "when passed an invalid uri" do 62 | it "returns an error string" do 63 | expect(URICleaner.clean("invalid uri")).to match /uri parse error/ 64 | end 65 | end 66 | 67 | context "when cleaning a list" do 68 | let (:uris) do 69 | [ 70 | "https://user:foobar@example.com/", 71 | "mailto:user@example.com", 72 | URI("nats://user:password@example.com:4222") 73 | ] 74 | end 75 | 76 | it "removes usernames and passwords from non-opaque uris" do 77 | expect(URICleaner.clean(uris)).to eq([ 78 | "https://example.com/", 79 | "mailto:user@example.com", 80 | "nats://example.com:4222" 81 | ]) 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /spec/unit/utils_spec.rb: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | require 'spec_helper' 4 | 5 | describe Dea do 6 | describe '.local_ip' do 7 | it 'returns an ip address to use' do 8 | expect(Dea.local_ip).to match(/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/) 9 | end 10 | end 11 | 12 | describe '.grab_ephemeral_port' do 13 | it 'returns a valid port' do 14 | expect(Dea.grab_ephemeral_port).to be_between(0, 65535).inclusive 15 | end 16 | end 17 | end 18 | --------------------------------------------------------------------------------